
Automate security and accelerate delivery: manage certificate pins from your pipeline and publish updates instantly, without blocking releases or waiting for app-store approval.
Two commands. trustpin-cli projects refresh-certs stages the pins TrustPin can see for a domain, from the live certificate and the Certificate Transparency logs. trustpin-cli projects sign signs the configuration and publishes it to the CDN. Nothing goes live until the signature succeeds, so the job is safe to run on a schedule.
export TRUSTPIN_API_TOKEN="tp_..." # from app.trustpin.cloud/account/access-tokens # Stage pins from the live certificate + CT logs (never removes a domain) trustpin-cli projects refresh-certs "$ORG_ID" "$PROJECT_ID" \ --domain api.example.com --remove-expired # Publish: master password (cloud keys) or --private-key key.pem (BYOK) trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" --password "$MASTER_PASSWORD" # Confirm what installed apps will fetch trustpin-cli projects jws "$ORG_ID" "$PROJECT_ID" --verify
Push pin updates instantly without shipping a new app release
Rotate certificates without service interruptions
Drop-in CI/CD pipeline integration
Centralized certificate management for your team
Any runner that can download a Linux or macOS binary. The examples below cover GitHub Actions, GitLab CI, Jenkins and Azure DevOps; the same three steps work in CircleCI, Bitrise or a plain cron job. Each one rehearses the signature with --dry-run first, so a wrong key or password fails the job before anything is staged.
name: Refresh certificate pins
on:
schedule:
- cron: "0 5 * * *" # daily 05:00 UTC
workflow_dispatch:
jobs:
refresh:
runs-on: ubuntu-latest
env:
TRUSTPIN_API_TOKEN: ${{ secrets.TRUSTPIN_API_TOKEN }}
ORG_ID: ${{ vars.TRUSTPIN_ORG_ID }}
PROJECT_ID: ${{ vars.TRUSTPIN_PROJECT_ID }}
MASTER_PASSWORD: ${{ secrets.TRUSTPIN_MASTER_PASSWORD }}
steps:
- name: Install TrustPin CLI
run: |
curl -sSL https://github.com/trustpin-cloud/homebrew-trustpin/releases/latest/download/trustpin-cli-linux-x64 -o trustpin-cli
chmod +x trustpin-cli && sudo mv trustpin-cli /usr/local/bin/
- name: Preflight the signing credentials (nothing is published)
run: trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" --password "$MASTER_PASSWORD" --dry-run > /dev/null
- name: Record the configuration version
id: before
run: |
VERSION=$(trustpin-cli projects get "$ORG_ID" "$PROJECT_ID" --output json | jq -r '.data.project.config_version')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Refresh pins from the live certificate and CT logs
run: |
trustpin-cli projects refresh-certs "$ORG_ID" "$PROJECT_ID" \
--domain api.example.com --remove-expired --output json
- name: Sign and publish only if something changed
run: |
AFTER=$(trustpin-cli projects get "$ORG_ID" "$PROJECT_ID" --output json | jq -r '.data.project.config_version')
if [ "$AFTER" = "${{ steps.before.outputs.version }}" ]; then
echo "Pins already up to date, nothing to publish."; exit 0
fi
trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" --password "$MASTER_PASSWORD"
trustpin-cli projects jws "$ORG_ID" "$PROJECT_ID" --verifyrefresh-pins:
image: ubuntu:24.04
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" # add a daily schedule in CI/CD > Schedules
- when: manual
variables:
ORG_ID: "$TRUSTPIN_ORG_ID" # CI/CD variables; mark the token and password as masked
PROJECT_ID: "$TRUSTPIN_PROJECT_ID"
before_script:
- apt-get update -qq && apt-get install -y -qq curl jq ca-certificates
- curl -sSL https://github.com/trustpin-cloud/homebrew-trustpin/releases/latest/download/trustpin-cli-linux-x64 -o trustpin-cli
- chmod +x trustpin-cli && mv trustpin-cli /usr/local/bin/
script:
- trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" --password "$TRUSTPIN_MASTER_PASSWORD" --dry-run > /dev/null
- BEFORE=$(trustpin-cli projects get "$ORG_ID" "$PROJECT_ID" --output json | jq -r '.data.project.config_version')
- trustpin-cli projects refresh-certs "$ORG_ID" "$PROJECT_ID" --domain api.example.com --remove-expired --output json
- AFTER=$(trustpin-cli projects get "$ORG_ID" "$PROJECT_ID" --output json | jq -r '.data.project.config_version')
- |
if [ "$BEFORE" = "$AFTER" ]; then echo "Pins already up to date"; exit 0; fi
trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" --password "$TRUSTPIN_MASTER_PASSWORD"
trustpin-cli projects jws "$ORG_ID" "$PROJECT_ID" --verifypipeline {
agent any
triggers { cron('H 5 * * *') }
environment {
TRUSTPIN_API_TOKEN = credentials('trustpin-api-token') // Secret text
MASTER_PASSWORD = credentials('trustpin-master-password') // Secret text
ORG_ID = 'fba3418e-b5ae-b273-4bab-6da6ae07ba99'
PROJECT_ID = '9caaea0a-80ea-013e-4e7b-cee6bfb52b36'
}
stages {
stage('Install CLI') {
steps { sh '''
curl -sSL https://github.com/trustpin-cloud/homebrew-trustpin/releases/latest/download/trustpin-cli-linux-x64 -o trustpin-cli
chmod +x trustpin-cli
''' }
}
stage('Preflight') {
steps { sh './trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" --password "$MASTER_PASSWORD" --dry-run > /dev/null' }
}
stage('Refresh and publish') {
steps { sh '''
BEFORE=$(./trustpin-cli projects get "$ORG_ID" "$PROJECT_ID" --output json | jq -r '.data.project.config_version')
./trustpin-cli projects refresh-certs "$ORG_ID" "$PROJECT_ID" --domain api.example.com --remove-expired --output json
AFTER=$(./trustpin-cli projects get "$ORG_ID" "$PROJECT_ID" --output json | jq -r '.data.project.config_version')
if [ "$BEFORE" = "$AFTER" ]; then echo "Pins already up to date"; exit 0; fi
./trustpin-cli projects sign "$ORG_ID" "$PROJECT_ID" --password "$MASTER_PASSWORD"
./trustpin-cli projects jws "$ORG_ID" "$PROJECT_ID" --verify
''' }
}
}
}schedules:
- cron: "0 5 * * *"
displayName: Daily pin refresh
branches: { include: [main] }
always: true
pool: { vmImage: ubuntu-latest }
variables:
- group: trustpin # TRUSTPIN_API_TOKEN and TRUSTPIN_MASTER_PASSWORD marked secret
- name: ORG_ID
value: fba3418e-b5ae-b273-4bab-6da6ae07ba99
- name: PROJECT_ID
value: 9caaea0a-80ea-013e-4e7b-cee6bfb52b36
steps:
- bash: |
curl -sSL https://github.com/trustpin-cloud/homebrew-trustpin/releases/latest/download/trustpin-cli-linux-x64 -o trustpin-cli
chmod +x trustpin-cli && sudo mv trustpin-cli /usr/local/bin/
displayName: Install TrustPin CLI
- bash: |
trustpin-cli projects sign "$(ORG_ID)" "$(PROJECT_ID)" --password "$MASTER_PASSWORD" --dry-run > /dev/null
BEFORE=$(trustpin-cli projects get "$(ORG_ID)" "$(PROJECT_ID)" --output json | jq -r '.data.project.config_version')
trustpin-cli projects refresh-certs "$(ORG_ID)" "$(PROJECT_ID)" --domain api.example.com --remove-expired --output json
AFTER=$(trustpin-cli projects get "$(ORG_ID)" "$(PROJECT_ID)" --output json | jq -r '.data.project.config_version')
if [ "$BEFORE" = "$AFTER" ]; then echo "Pins already up to date"; exit 0; fi
trustpin-cli projects sign "$(ORG_ID)" "$(PROJECT_ID)" --password "$MASTER_PASSWORD"
trustpin-cli projects jws "$(ORG_ID)" "$(PROJECT_ID)" --verify
displayName: Refresh, sign and verify pins
env:
TRUSTPIN_API_TOKEN: $(TRUSTPIN_API_TOKEN)
MASTER_PASSWORD: $(TRUSTPIN_MASTER_PASSWORD)Bring Your Own Keys: write the PEM private key from a secret to a temporary file and replace --password "$MASTER_PASSWORD" with --private-key "$PRIVATE_KEY_FILE". The dry run validates the key against the project's registered public key.
Internal hosts TrustPin cannot observe: extract the SPKI pin yourself and stage it with projects upsert. The DevOps guide covers both paths, plus an AWS ACM renewal workflow with EventBridge and Lambda.
Stage, then publish. refresh-certs never discards an existing pin and writes nothing if the lookup fails, so a DNS blip can never strip a domain. Because it returns every unexpired issuance, a renewed certificate is pinned alongside the one still being served: the dual-pin overlap that makes rotation safe, maintained without bookkeeping.
Add the TrustPin SDK to your app once; every later pin change is remote
refresh-certs stages the pins TrustPin can see for each domain, on a schedule
sign publishes the configuration with your master password or BYOK private key
Installed apps fetch the signed configuration within minutes
Add the TrustPin SDK to your app once; every later pin change is remote
refresh-certs stages the pins TrustPin can see for each domain, on a schedule
sign publishes the configuration with your master password or BYOK private key
Installed apps fetch the signed configuration within minutes
Start free, add the CLI to one pipeline, and let the schedule keep your pins current.