DevOps Guide
Automate certificate pin management in production environments using the TrustPin CLI. This guide covers the complete workflow for managing certificate pins from installation to automated CI/CD deployments.
Overview
TrustPin uses a two-step process for certificate pin updates:
- Upsert: Add or update certificate pins in your project configuration
- Sign: Cryptographically sign the configuration and publish it to the CDN
This separation allows you to:
- Stage multiple pin changes before publishing
- Review configuration changes before they go live
- Maintain an audit trail of all pin modifications
- Use different security controls for staging vs. publishing
Quick Start
1. Install the CLI
Homebrew (Recommended):
brew tap trustpin-cloud/trustpin-cli
brew install trustpin-cliDirect Download (Linux x64 for CI/CD):
curl -L https://github.com/trustpin-cloud/cloud-console-cli/releases/latest/download/trustpin-cli-linux-x64 -o trustpin-cli
chmod +x trustpin-cli
sudo mv trustpin-cli /usr/local/bin/See Installation for platform-specific instructions.
2. Configure Authentication
Set the TRUSTPIN_API_TOKEN environment variable:
export TRUSTPIN_API_TOKEN="tp_your_token_here"Get your Personal Access Token from TrustPin Console .
The CLI automatically uses this environment variable when available.
3. Get Your Project IDs
# List all projects
trustpin-cli projects list
# Get IDs in JSON format
trustpin-cli projects list --output json | jq -r '.data.projects[] | "\(.organization_id) \(.id) \(.name)"'Note your Organization ID and Project ID for subsequent commands.
Certificate Pin Management Workflow
Step 1: Extract Pin from Certificate
Extract the SPKI SHA-256 fingerprint and expiration date from your certificate:
#!/bin/bash
CERT_FILE="cert.pem"
# Extract SPKI SHA-256 (recommended - survives certificate renewal with same key)
SPKI_SHA256=$(openssl x509 -in "$CERT_FILE" -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -binary | \
base64)
# Extract expiration date
EXPIRES=$(openssl x509 -in "$CERT_FILE" -noout -enddate | \
cut -d= -f2 | \
xargs -I{} date -d "{}" -u +%Y-%m-%dT%H:%M:%SZ)
echo "SPKI SHA-256: $SPKI_SHA256"
echo "Expires: $EXPIRES"Example output:
SPKI SHA-256: oxVSdCLgthL3M5Vnnzepq8WWlkUfRPYkpjLpm+wn+1o=
Expires: 2026-04-13T08:37:02ZStep 2: Upsert the Pin
Add or update the certificate pin in your project configuration:
trustpin-cli projects upsert \
<organization-id> \
<project-id> \
--domain api.example.com \
--pin spki-sha256:$SPKI_SHA256 \
--expires $EXPIRES \
--output jsonExample output:
{
"status": "success",
"operation": "upsert",
"domain": "api.example.com",
"action": "added",
"pin": {
"type": "spki-sha256",
"value": "oxVSdCLgthL3M5Vnnzepq8WWlkUfRPYkpjLpm+wn+1o=",
"expires_at": "2026-04-13T08:37:02Z"
},
"config_version": {
"before": 2,
"after": 3
}
}Note: The configuration version increments, but changes are not yet published.
Step 3: Sign and Publish
Sign the configuration and publish it to the CDN:
Cloud-Managed Keys:
trustpin-cli projects sign \
<organization-id> \
<project-id> \
--password <master-password>Bring Your Own Key (BYOK):
trustpin-cli projects sign \
<organization-id> \
<project-id> \
--private-key path/to/private-key.pemExample output:
[1/5] Getting project information
Project: Production API (Managed CDN with Cloud Keys)
[2/5] Loading project configuration
Configuration loaded with 3 domains
[3/5] Preparing private key
Using cloud-managed private key
[4/5] Creating and signing JWT
JWT signed successfully
[5/5] Uploading signed configuration
✅ Configuration published successfully!The signed configuration is now live and available to your mobile apps.
Complete Workflow Script
This script automates the entire process with error handling:
#!/bin/bash
set -e
ORG_ID="fb52418e-b5ae-4bff-b973-6da9ae07ba00"
PROJECT_ID="9caaea0a-013e-4e7b-80ea-cee6bfb52b36"
DOMAIN="api.example.com"
CERT_FILE="certs/api-cert.pem"
echo "═══ Step 1: Extract Pin from Certificate ═══"
SPKI_SHA256=$(openssl x509 -in "$CERT_FILE" -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -binary | \
base64)
EXPIRES=$(openssl x509 -in "$CERT_FILE" -noout -enddate | \
cut -d= -f2 | \
xargs -I{} date -d "{}" -u +%Y-%m-%dT%H:%M:%SZ)
echo " SPKI SHA-256: $SPKI_SHA256"
echo " Expires: $EXPIRES"
echo ""
echo "═══ Step 2: Upsert Certificate Pin ═══"
RESULT=$(trustpin-cli projects upsert "$ORG_ID" "$PROJECT_ID" \
--domain "$DOMAIN" \
--pin "spki-sha256:$SPKI_SHA256" \
--expires "$EXPIRES" \
--output json)
echo "$RESULT" | jq '.'
# Verify success
STATUS=$(echo "$RESULT" | jq -r '.status')
if [ "$STATUS" != "success" ]; then
echo "❌ Failed to upsert pin"
exit 1
fi
ACTION=$(echo "$RESULT" | jq -r '.action')
echo "✅ Pin ${ACTION} successfully"
echo ""
echo "═══ Step 3: Sign and Publish Configuration ═══"
trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" \
--password "$MASTER_PASSWORD"
echo ""
echo "✅ Certificate pin updated and published!"Advanced Use Cases
Verifying Published Configuration
After signing and publishing, verify the configuration is correctly deployed to the CDN:
# Verify JWS signature using public key from CDN
trustpin-cli projects jws $ORG_ID $PROJECT_ID --verify
# Decode and inspect published configuration
trustpin-cli projects jws $ORG_ID $PROJECT_ID --decode
# Save published JWS for archival/compliance
trustpin-cli projects jws $ORG_ID $PROJECT_ID \
--output-file "archive/config-$(date +%Y%m%d-%H%M%S).jws"Why validation matters:
- Confirms the
signcommand successfully published to CDN - Catches CDN propagation delays or failures
- Provides audit trail of published configurations
- Enables comparison between database and CDN state
Example validation script:
#!/bin/bash
set -e
ORG_ID="fb52418e-b5ae-4bff-b973-6da9ae07ba00"
PROJECT_ID="9caaea0a-013e-4e7b-80ea-cee6bfb52b36"
echo "Verifying published configuration..."
# Check if JWS is published
JWS=$(trustpin-cli projects jws $ORG_ID $PROJECT_ID 2>/dev/null)
if [ -z "$JWS" ]; then
echo "❌ No JWS published on CDN"
exit 1
fi
# Verify signature
if ! trustpin-cli projects jws $ORG_ID $PROJECT_ID --verify &>/dev/null; then
echo "❌ JWS signature verification failed"
exit 5
fi
echo "✅ JWS signature valid"
# Check version alignment
trustpin-cli projects jws $ORG_ID $PROJECT_ID --decode | grep -E "(Current DB Version|Published Version)"
echo "✅ Published configuration validated"Self-Hosted CDN Deployment
For organizations hosting JWS configurations on their own infrastructure:
Workflow:
- Use TrustPin to manage and sign configurations (upsert + sign)
- Download the signed JWS from TrustPin CDN
- Deploy to your own CDN/servers
- Configure mobile SDKs to fetch from your infrastructure
Deployment script:
#!/bin/bash
set -e
ORG_ID="fb52418e-b5ae-4bff-b973-6da9ae07ba00"
PROJECT_ID="9caaea0a-013e-4e7b-80ea-cee6bfb52b36"
CDN_PATH="/var/www/cdn/trustpin"
echo "═══ Step 1: Upsert Certificate Pins ═══"
# Update your pins...
trustpin-cli projects upsert $ORG_ID $PROJECT_ID \
--domain api.example.com \
--pin spki-sha256:$SPKI_SHA256 \
--expires $EXPIRES
echo ""
echo "═══ Step 2: Sign Configuration ═══"
trustpin-cli projects sign $ORG_ID $PROJECT_ID \
--password "$MASTER_PASSWORD"
echo ""
echo "═══ Step 3: Download Signed JWS ═══"
trustpin-cli projects jws $ORG_ID $PROJECT_ID \
--output-file "$CDN_PATH/config.jws"
echo ""
echo "═══ Step 4: Verify JWS Integrity ═══"
if ! trustpin-cli projects jws $ORG_ID $PROJECT_ID --verify; then
echo "❌ JWS verification failed - aborting deployment"
exit 5
fi
echo ""
echo "═══ Step 5: Deploy to CDN ═══"
# Example: Upload to S3, CloudFront, or your CDN
aws s3 cp "$CDN_PATH/config.jws" s3://your-bucket/trustpin/config.jws \
--cache-control "public, max-age=3600" \
--metadata "version=$(date +%s)"
# Invalidate CDN cache if needed
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths "/trustpin/config.jws"
echo "✅ JWS deployed to self-hosted CDN"Benefits:
- Full control over CDN infrastructure and caching
- Compliance with data residency requirements
- Integration with existing CDN/monitoring systems
- Custom cache invalidation strategies
Troubleshooting
Authentication Issues
Problem: HTTP 401 Unauthorized
Solutions:
# Verify token is set correctly
echo $TRUSTPIN_API_TOKEN
# Test authentication
trustpin-cli projects list --output json
# Reconfigure CLI
trustpin-cli configureIn CI/CD:
- Verify
TRUSTPIN_API_TOKENsecret is set correctly - Ensure token hasn’t expired (create new token if needed)
- Check token has access to the organization/project
Validation Errors
Problem: Invalid pin format or type
❌ Validation failed: invalid pin type: sha255
Valid types: sha256, sha512, spki-sha256, spki-sha512Solution: Fix typo in pin type (e.g., sha255 → sha256)
Problem: Invalid base64 encoding
❌ Validation failed: invalid base64 encoding in pin valueSolution: Ensure hash is base64-encoded, not hex:
# Correct: base64 encoding
openssl dgst -sha256 -binary | base64
# Incorrect: hex encoding
openssl dgst -sha256Problem: Wrong hash length
❌ Validation failed: invalid sha256 hash length: expected 32 bytes, got 16 bytesSolution: Verify you’re hashing the correct data:
# For SPKI pins (correct)
openssl x509 -in cert.pem -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -binary | base64
# For certificate pins (correct)
openssl x509 -in cert.pem -outform der | \
openssl dgst -sha256 -binary | base64Private Key Issues (BYOK)
Problem: Failed to decrypt private key
❌ Failed to decrypt private key: cipher: message authentication failedSolutions:
- Verify master password is correct for cloud-managed keys
- Verify private key password is correct for BYOK
- Check private key file is in PEM format:
file private-key.pem
Problem: Private key format errors in CI/CD
# Verify key is properly decoded
file private-key.pem
# Should show: "PEM RSA private key" or similar
# Check file permissions
ls -la private-key.pem
# Should be 600 or 400Network Issues
Problem: Connection timeout or network errors
Solutions:
# Test API connectivity
curl -H "Authorization: Bearer $TRUSTPIN_API_TOKEN" \
https://api.trustpin.cloud/users
# Check API base URL
echo $TRUSTPIN_API_BASE_URL
# Use verbose mode for debugging
trustpin-cli projects sign <org-id> <project-id> --verboseUpsert Operation Issues
Problem: Upsert succeeds but nothing changes
Reason: The pin already exists with the same expiration (idempotent operation)
Response:
{
"status": "success",
"operation": "upsert",
"action": "no_change",
"message": "Pin already exists with same expiration"
}Solution: This is expected behavior. No action needed.
Exit Codes
The CLI uses standard exit codes for automation:
| Code | Meaning | Example |
|---|---|---|
| 0 | Success or no-op | Pin added/updated, or already exists |
| 2 | API error | 401, 404, 500 HTTP errors |
| 4 | Validation error | Invalid domain, pin format, or type |
| 99 | Unexpected error | Network timeout, file I/O error |
Use in scripts:
if ! trustpin-cli projects upsert ... --output json > result.json; then
EXIT_CODE=$?
echo "❌ Upsert failed with exit code $EXIT_CODE"
cat result.json
exit $EXIT_CODE
fiGetting Help
View command help:
trustpin-cli --help
trustpin-cli projects --help
trustpin-cli projects upsert --help
trustpin-cli projects sign --helpEnable verbose output:
trustpin-cli projects upsert ... --verbose
trustpin-cli projects sign ... --verboseCheck CLI version:
trustpin-cli --versionFor additional support, contact support@trustpin.cloud or visit the TrustPin Console .