Commands
Complete reference for TrustPin CLI commands.
Authentication
configure
Configure the CLI with your Personal Access Token.
trustpin-cli configureInteractive prompt:
$ trustpin-cli configure
API Base URL (https://api.trustpin.cloud): https://api.trustpin.cloud
API Token: tp_your_personal_access_token_here
✓ Configuration saved successfully!Get your Personal Access Token:
- Visit https://app.trustpin.cloud/account/access-tokens
- Click Create Token
- Copy the token (it starts with
tp_) - Use it with
trustpin-cli configure
Token permissions:
- User-scoped: Access all projects you have permission to view
- Can expire: Create a new token if yours expires
Reset configuration:
If you need to reconfigure or clear your credentials:
# Remove configuration
rm -rf ~/.trustpin/
# Configure again
trustpin-cli configureProject Management
projects list
List all projects you have access to.
trustpin-cli projects list [--output json]Examples:
# Human-readable format (default)
$ trustpin-cli projects list
═══ Projects (2) ═══
── TrustPin Mobile App ──
ID: df9964a9-66bf-4673-9743-adee9ce6213e
Type: Managed CDN with Cloud Keys
Organization: Personal Organization
Domains: 4
── API Gateway ──
ID: 99acf096-79b9-4fa6-a115-14b35b224839
Type: Managed CDN with Bring Your Own Keys
Organization: Personal Organization
Domains: 2# JSON format for automation
$ trustpin-cli projects list --output json
{
"status": "success",
"operation": "projects-list",
"data": {
"projects": [
{
"id": "df9964a9-66bf-4673-9743-adee9ce6213e",
"name": "TrustPin Mobile App",
"organization_id": "fb52418e-b5ae-4bff-b973-6da9ae07ba00",
"organization_name": "Personal Organization",
"type": "Managed CDN with Cloud Keys",
"domains_count": 4
}
]
}
}Use with jq:
# Get all project IDs
trustpin-cli projects list --output json | jq -r '.data.projects[].id'
# Get project names
trustpin-cli projects list --output json | jq -r '.data.projects[].name'projects upsert
Add or update a certificate pin for a domain in your project configuration.
trustpin-cli projects upsert <organization-id> <project-id> \
--domain <domain-name> \
--pin <type>:<value> \
[--expires <ISO8601-datetime>] \
[--dry-run] \
[--output json]Parameters:
| Parameter | Required | Description |
|---|---|---|
<organization-id> | Yes | Organization UUID |
<project-id> | Yes | Project UUID |
--domain | Yes | Domain name (e.g., api.example.com) |
--pin | Yes | Pin in format <type>:<value> |
--expires | No | Expiration date in ISO 8601 format |
--dry-run | No | Preview changes without applying them |
--output | No | Output format: human (default) or json |
Pin Types:
| Type | Description | Use Case |
|---|---|---|
sha256 | Certificate SHA-256 fingerprint | Must update pin on every certificate renewal |
sha512 | Certificate SHA-512 fingerprint | Must update pin on every certificate renewal |
spki-sha256 | SPKI SHA-256 fingerprint | Recommended - OWASP best practice |
spki-sha512 | SPKI SHA-512 fingerprint | Maximum security + OWASP best practice |
Why SPKI is recommended: SPKI (Subject Public Key Info) pins the public key, not the certificate. This is the OWASP-recommended approach for certificate pinning. When combined with TrustPin’s dynamic pin updates, it enables zero-downtime certificate rotation regardless of whether keys change during renewal.
Key Reuse (Optional): If you reuse the same private key during certificate renewal (e.g., certbot renew --reuse-key), the SPKI pin remains unchanged. However, most managed certificate services (AWS ACM, Let’s Encrypt default, Cloudflare) generate new key pairs on renewal, which changes the SPKI pin—but TrustPin’s dynamic updates handle this seamlessly.
Upsert Behavior:
The command is idempotent and follows these rules:
| Scenario | Action | Response |
|---|---|---|
| Domain exists, pin exists, same expiration | No-op | "action": "no_change" |
| Domain exists, pin exists, different expiration | Update | "action": "updated" |
| Domain exists, pin doesn’t exist | Add | "action": "added" |
| Domain doesn’t exist | Create + Add | "action": "added" |
Pin matching: Pins are matched by type + value. Expiration is not part of the match key.
Examples:
Extract and add SPKI SHA-256 pin:
# Extract SPKI SHA-256 from certificate
SPKI_SHA256=$(openssl x509 -in cert.pem -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -binary | \
base64)
# Extract expiration date
EXPIRES=$(openssl x509 -in cert.pem -noout -enddate | \
cut -d= -f2 | \
xargs -I{} date -d "{}" -u +%Y-%m-%dT%H:%M:%SZ)
# Add pin to project
trustpin-cli projects upsert \
fb52418e-b5ae-4bff-b973-6da9ae07ba00 \
9caaea0a-013e-4e7b-80ea-cee6bfb52b36 \
--domain api.example.com \
--pin spki-sha256:$SPKI_SHA256 \
--expires $EXPIRES \
--output jsonOutput:
{
"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
}
}Dry run to preview changes:
trustpin-cli projects upsert \
fb52418e-b5ae-4bff-b973-6da9ae07ba00 \
9caaea0a-013e-4e7b-80ea-cee6bfb52b36 \
--domain api.example.com \
--pin spki-sha256:$SPKI_SHA256 \
--expires 2026-04-13T08:37:02Z \
--dry-runOutput (dry run):
Dry run mode enabled - no changes will be made
Would send PATCH request to:
/organizations/fb52418e-b5ae-4bff-b973-6da9ae07ba00/projects/9caaea0a-013e-4e7b-80ea-cee6bfb52b36/config
Request body:
{
"domain": "api.example.com",
"pin": {
"type": "spki-sha256",
"value": "oxVSdCLgthL3M5Vnnzepq8WWlkUfRPYkpjLpm+wn+1o=",
"expires_at": "2026-04-13T08:37:02Z"
}
}Update expiration date only:
# Pin already exists, just update the expiration
trustpin-cli projects upsert \
fb52418e-b5ae-4bff-b973-6da9ae07ba00 \
9caaea0a-013e-4e7b-80ea-cee6bfb52b36 \
--domain api.example.com \
--pin spki-sha256:oxVSdCLgthL3M5Vnnzepq8WWlkUfRPYkpjLpm+wn+1o= \
--expires 2027-06-01T00:00:00ZOutput:
{
"status": "success",
"operation": "upsert",
"domain": "api.example.com",
"action": "updated",
"pin": {
"type": "spki-sha256",
"value": "oxVSdCLgthL3M5Vnnzepq8WWlkUfRPYkpjLpm+wn+1o=",
"expires_at": "2027-06-01T00:00:00Z"
},
"config_version": {
"before": 3,
"after": 4
}
}Add certificate SHA-256 pin:
# Extract certificate SHA-256
CERT_SHA256=$(openssl x509 -in cert.pem -outform der | \
openssl dgst -sha256 -binary | \
base64)
trustpin-cli projects upsert \
fb52418e-b5ae-4bff-b973-6da9ae07ba00 \
9caaea0a-013e-4e7b-80ea-cee6bfb52b36 \
--domain cdn.example.com \
--pin sha256:$CERT_SHA256 \
--expires 2026-04-13T08:37:02ZImportant Notes:
- Configuration is not published yet - After upserting, you must run
projects signto publish changes to the CDN - Multiple upserts before signing - You can upsert multiple domains/pins before signing once
- Idempotent operation - Safe to run multiple times; won’t duplicate pins
- Version tracking - Each upsert increments the configuration version
- No waiting period - Upsert is instantaneous; only signing publishes to CDN
Common Use Cases:
1. Add backup pin before certificate rotation:
# Add new certificate's pin while old cert is still active
trustpin-cli projects upsert $ORG_ID $PROJECT_ID \
--domain api.example.com \
--pin spki-sha256:$NEW_SPKI \
--expires 2027-06-01T00:00:00Z
# Sign and publish
trustpin-cli projects sign $ORG_ID $PROJECT_ID --password $MASTER_PASSWORD
# Wait 24-48 hours for mobile apps to fetch new config
# Then deploy new certificate to servers2. Update multiple domains with wildcard certificate:
# Same pin for multiple domains
for DOMAIN in api.example.com cdn.example.com www.example.com; do
trustpin-cli projects upsert $ORG_ID $PROJECT_ID \
--domain $DOMAIN \
--pin spki-sha256:$SPKI \
--expires $EXPIRES
done
# Sign once after all updates
trustpin-cli projects sign $ORG_ID $PROJECT_ID --password $MASTER_PASSWORD3. Certificate renewal with same key (SPKI unchanged):
# Only expiration changes, SPKI remains the same
trustpin-cli projects upsert $ORG_ID $PROJECT_ID \
--domain api.example.com \
--pin spki-sha256:$EXISTING_SPKI \
--expires $NEW_EXPIRES
# Sign and deploy immediately (no waiting needed - pin unchanged)
trustpin-cli projects sign $ORG_ID $PROJECT_ID --password $MASTER_PASSWORDExit Codes:
| Code | Meaning | Example |
|---|---|---|
| 0 | Success or no-op | Pin added, updated, or unchanged |
| 2 | API error | HTTP 401, 404, 500 |
| 4 | Validation error | Invalid domain, pin format, or type |
| 99 | Unexpected error | Network timeout, file I/O error |
See Also:
- DevOps Guide - Complete workflow with examples
- Projects Sign - Publishing configurations
Configuration Signing
projects sign (Cloud Keys)
Sign and publish project configuration using cloud-managed keys.
trustpin-cli projects sign <organization-id> <project-id>Example:
$ trustpin-cli projects sign fb52418e-b5ae-4bff-b973-6da9ae07ba00 df9964a9-66bf-4673-9743-adee9ce6213e
Master password: ****
[1/5] Getting project information
✓ Project: TrustPin Mobile App (Managed CDN with Cloud Keys)
[2/5] Loading project configuration
✓ Configuration loaded with 4 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 configuration is now live and SDKs will use it immediately.
projects sign (BYOK)
Sign and publish project configuration using your own private key (Bring Your Own Key).
trustpin-cli projects sign <organization-id> <project-id> --private-key <path-to-pem-file>Requirements:
- Private key must be in PEM format
- Private key can be password-protected (you’ll be prompted)
Example:
$ trustpin-cli projects sign fb52418e-b5ae-4bff-b973-6da9ae07ba00 99acf096-79b9-4fa6-a115-14b35b224839 --private-key ./my-private-key.pem
Master password: ****
Private key password: ****
[1/5] Getting project information
✓ Project: API Gateway (Managed CDN with Bring Your Own Keys)
[2/5] Loading project configuration
✓ Configuration loaded with 2 domains
[3/5] Preparing private key
✓ Reading private key from file: ./my-private-key.pem
[4/5] Creating and signing JWT
✓ JWT signed successfully
[5/5] Uploading signed configuration
✓ Configuration published successfully!What happens on failure:
If signing fails, the previous configuration remains active. Your users are not affected. You can retry signing at any time.
projects jws
Fetch the published JWS (JSON Web Signature) configuration from the CDN.
This command retrieves the currently published certificate pinning configuration that mobile applications are fetching. The JWS is the signed configuration that was published by the sign command.
trustpin-cli projects jws <organization-id> <project-id> [flags]Flags:
| Flag | Description |
|---|---|
--decode | Decode and display JWS header and payload |
--verify | Verify JWS signature using public key from CDN |
--output-file <path> | Save JWS to file |
Use cases:
- Verify what configuration is currently published
- Debug mobile app pinning issues
- Validate that
signsuccessfully published the configuration - Download the JWS for testing, archival, or self-hosted CDN deployment
Examples:
Fetch raw JWS (default):
$ trustpin-cli projects jws fb52418e-b5ae-4bff-b973-6da9ae07ba00 df9964a9-66bf-4673-9743-adee9ce6213e
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJkb21haW5zIjp7ImFwaS5leGFtcGxlLmNvbSI6W3sidHlwZSI6InNwa2ktc2hhMjU2IiwidmFsdWUiOiJveFZTZENMZ3RoTDNNNVZubnplcHE4V1dsa1VmUlBZa3BqTHBtK3duKzFvPSIsImV4cGlyZXNfYXQiOiIyMDI2LTA0LTEzVDA4OjM3OjAyWiJ9XX0sImlhdCI6MTczODk4NzIwMH0.kG7z2x3nF4mB8qW9pL5jD3rC6vT8sY1eN4hK2mO9iU7lP4aQ6wX8bV9nE5cR3fJ2dT7gH1sM4kL6pO8jU9qW3eVerify JWS signature:
$ trustpin-cli projects jws fb52418e-b5ae-4bff-b973-6da9ae07ba00 df9964a9-66bf-4673-9743-adee9ce6213e --verify
✓ JWS signature valid
✓ Signed with algorithm: ES256 (ECDSA P-256)
✓ Public key verified from CDNDecode and display payload:
$ trustpin-cli projects jws fb52418e-b5ae-4bff-b973-6da9ae07ba00 df9964a9-66bf-4673-9743-adee9ce6213e --decode
═══ JWS Header ═══
{
"alg": "ES256",
"typ": "JWT"
}
═══ JWS Payload ═══
{
"domains": {
"api.example.com": [
{
"type": "spki-sha256",
"value": "oxVSdCLgthL3M5Vnnzepq8WWlkUfRPYkpjLpm+wn+1o=",
"expires_at": "2026-04-13T08:37:02Z"
}
]
},
"iat": 1738987200
}
Current DB Version: 3
Published Version: 3Save JWS to file:
$ trustpin-cli projects jws fb52418e-b5ae-4bff-b973-6da9ae07ba00 df9964a9-66bf-4673-9743-adee9ce6213e --output-file config.jws
✓ JWS saved to config.jwsPipe to mobile app simulator:
$ trustpin-cli projects jws fb52418e-b5ae-4bff-b973-6da9ae07ba00 df9964a9-66bf-4673-9743-adee9ce6213e | ./test-mobile-app
Testing certificate pinning with config...
✓ All domains validated successfullyCommon workflows:
1. Verify deployment after signing:
# Sign configuration
trustpin-cli projects sign $ORG_ID $PROJECT_ID --password $MASTER_PASSWORD
# Verify it's published and valid
trustpin-cli projects jws $ORG_ID $PROJECT_ID --verify2. Archive configurations for compliance:
# Save with timestamp
trustpin-cli projects jws $ORG_ID $PROJECT_ID \
--output-file "archive/config-$(date +%Y%m%d-%H%M%S).jws"3. Deploy to self-hosted CDN:
# Download signed JWS
trustpin-cli projects jws $ORG_ID $PROJECT_ID --output-file config.jws
# Verify integrity
trustpin-cli projects jws $ORG_ID $PROJECT_ID --verify
# Upload to your CDN
aws s3 cp config.jws s3://your-bucket/trustpin/config.jwsExit Codes:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | No JWS published on CDN |
| 2 | API error |
| 5 | JWS signature verification failed |
| 99 | Unexpected error |
See Also:
- DevOps Guide - Advanced Use Cases - Self-hosted CDN deployment
- Projects Sign - Publishing configurations
Output Formats
All commands support two output formats:
Human-Readable (Default)
Formatted output with colors and progress indicators, perfect for interactive use.
trustpin-cli projects listJSON
Machine-readable output for automation and CI/CD pipelines.
trustpin-cli projects list --output jsonJSON response structure:
{
"status": "success",
"operation": "command-name",
"data": {
// Command-specific data
}
}Getting Help
View help for any command:
# General help
trustpin-cli --help
# Command-specific help
trustpin-cli projects --help
trustpin-cli projects sign --help