CLI Workflows
Task-oriented guide for common CLI workflows including signing, verification, and CI/CD integration.
Signing and Verifying Archives
Keyless Signing (Recommended for CI)
In GitHub Actions or other OIDC-enabled CI environments, use keyless signing:
# Push and sign in one step
blob push --sign ghcr.io/myorg/archive:v1 ./src
# Or sign separately after pushing
blob push ghcr.io/myorg/archive:v1 ./src
blob sign ghcr.io/myorg/archive:v1
Keyless signing uses ambient OIDC credentials from the CI environment. The signature includes the workflow identity, enabling verification that archives came from specific workflows.
Key-Based Signing
For environments without OIDC, use a private key:
# Sign with a private key
blob sign --key private.pem ghcr.io/myorg/archive:v1
Generate a signing key:
openssl ecparam -genkey -name prime256v1 -out private.pem
openssl ec -in private.pem -pubout -out public.pem
Verifying Signatures
Verify that an archive was signed by a trusted source:
# Verify signature from any workflow in the repo
blob verify --repo=myorg/myrepo ghcr.io/myorg/archive:v1
# Verify with branch restriction
blob verify --repo=myorg/myrepo --branches=main ghcr.io/myorg/archive:v1
# Verify with tag restriction
blob verify --repo=myorg/myrepo --tags="v*" ghcr.io/myorg/archive:v1
# Combined restrictions (main branch OR release tags)
blob verify --repo=myorg/myrepo --branches=main --tags="v*" \
ghcr.io/myorg/archive:v1
Enforcing Verification on Pull
Always verify before extracting:
# Pull with verification required
blob pull --verify --policy=policy.yaml ghcr.io/myorg/archive:v1 ./dest
Policy-Based Verification
Creating a Policy File
Define verification requirements in a policy file:
# policy.yaml - Require GitHub Actions signature from main branch or release tags
signature:
issuer: https://token.actions.githubusercontent.com
subject_regex: "^https://github.com/myorg/myrepo/\\.github/workflows/.*@refs/(heads/main|tags/v.*)$"
provenance:
source_repo: https://github.com/myorg/myrepo
branches:
- main
tags:
- "v*"
Using Policies
# Verify with policy
blob verify --policy=policy.yaml ghcr.io/myorg/archive:v1
# Pull with policy enforcement
blob pull --verify --policy=policy.yaml ghcr.io/myorg/archive:v1 ./dest
Policy Examples
Strict production policy:
# production-policy.yaml
signature:
issuer: https://token.actions.githubusercontent.com
# Only release workflow on main branch
subject_regex: "^https://github.com/myorg/myrepo/\\.github/workflows/release\\.yml@refs/heads/main$"
provenance:
builder: https://github.com/slsa-framework/slsa-github-generator
source_repo: https://github.com/myorg/myrepo
branches:
- main
Multi-repo policy:
# team-policy.yaml
signature:
issuer: https://token.actions.githubusercontent.com
# Allow any repo in the org
subject_regex: "^https://github.com/myorg/.*"
provenance:
source_repo_regex: "^https://github.com/myorg/.*"
tags:
- "v*"
CI/CD Integration
GitHub Actions: Build and Sign
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
permissions:
contents: read
packages: write
id-token: write # Required for keyless signing
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install blob CLI
run: curl -sSfL https://blob.meigma.dev/install.sh | sh
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build assets
run: |
mkdir -p dist
# Your build steps here
cp -r ./assets ./dist/
- name: Push and sign archive
run: |
blob push --sign --compression=zstd \
-t latest \
-a "org.opencontainers.image.source=https://github.com/${{ github.repository }}" \
-a "org.opencontainers.image.revision=${{ github.sha }}" \
ghcr.io/${{ github.repository }}/assets:${{ github.ref_name }} \
./dist
GitHub Actions: Verified Pull
# .github/workflows/deploy.yml
name: Deploy
on:
workflow_dispatch:
inputs:
version:
description: 'Version to deploy'
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Install blob CLI
run: curl -sSfL https://blob.meigma.dev/install.sh | sh
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull with verification
run: |
blob pull --verify \
--repo=${{ github.repository }} \
--branches=main \
--tags="v*" \
ghcr.io/${{ github.repository }}/assets:${{ inputs.version }} \
./deploy
- name: Deploy
run: |
# Your deployment steps
rsync -av ./deploy/ /var/www/app/
GitLab CI: Build and Sign
# .gitlab-ci.yml
stages:
- build
- release
build:
stage: build
script:
- mkdir -p dist
- # Your build steps
artifacts:
paths:
- dist/
release:
stage: release
image: golang:1.21
id_tokens:
SIGSTORE_ID_TOKEN:
aud: sigstore
script:
- curl -sSfL https://blob.meigma.dev/install.sh | sh
- blob push --sign --compression=zstd
registry.gitlab.com/$CI_PROJECT_PATH/assets:$CI_COMMIT_TAG
./dist
rules:
- if: $CI_COMMIT_TAG
Using with Docker Compose
# docker-compose.yml
services:
app:
build: .
volumes:
- assets:/app/assets
assets-sync:
image: alpine
command: |
sh -c "
curl -sSfL https://blob.meigma.dev/install.sh | sh
blob pull --verify --repo=myorg/myrepo \
ghcr.io/myorg/assets:latest /app/assets
"
volumes:
- assets:/app/assets
volumes:
assets:
Using Aliases for Efficiency
Setting Up Aliases
# Production registry
blob alias add prod ghcr.io/myorg/production
# Staging registry
blob alias add staging ghcr.io/myorg/staging
# Development (local registry)
blob alias add dev localhost:5000/dev
Using Aliases
# Push to production
blob push prod/assets:v1.0.0 ./dist
# Pull from staging
blob pull staging/assets:latest ./deploy
# Compare versions
blob inspect prod/assets:v1.0.0
blob inspect staging/assets:v1.0.0
Managing Aliases
# List all aliases
blob alias list
# Remove an alias
blob alias rm dev
# Aliases in config file
blob config show
Cache Management
Enabling Caching
# Enable caching (one-time setup)
blob config set cache.dir ~/.cache/blob
Monitoring Cache
# View cache statistics
blob cache status
# Example output:
# CACHE SIZE MAX ENTRIES
# refs 1.2 KB 5 MB 15
# manifests 45 KB 10 MB 12
# indexes 2.1 MB 50 MB 8
# content 89 MB 100 MB 1,247
# blocks 12 MB 50 MB 892
Clearing Caches
# Clear all caches
blob cache clear
# Clear specific cache layer
blob cache clear content # File content cache
blob cache clear blocks # HTTP range block cache
blob cache clear refs # Tag resolution cache
blob cache clear manifests # Manifest cache
blob cache clear indexes # Index blob cache
Cache Configuration
# Set reference cache TTL (for mutable tags like 'latest')
blob config set cache.ref_ttl 5m
# Set cache size limits
blob config set cache.content_max 500M
blob config set cache.blocks_max 100M
CI/CD Caching
In CI environments, cache the blob cache directory:
GitHub Actions:
- uses: actions/cache@v4
with:
path: ~/.cache/blob
key: blob-cache-${{ runner.os }}
restore-keys: |
blob-cache-
GitLab CI:
cache:
paths:
- .cache/blob/
variables:
BLOB_CACHE_DIR: .cache/blob
Scripting with JSON Output
Use JSON output for scripting and automation:
# Get archive info as JSON
blob inspect -o json ghcr.io/myorg/archive:v1 | jq '.digest'
# List files as JSON
blob ls -o json ghcr.io/myorg/archive:v1 | jq '.[].path'
# Check file count
blob inspect -o json ghcr.io/myorg/archive:v1 | jq '.file_count'
# Get specific file sizes
blob ls -o json ghcr.io/myorg/archive:v1 | \
jq -r '.[] | select(.path | startswith("src/")) | "\(.path): \(.size)"'
Example: Diff Two Archives
#!/bin/bash
# diff-archives.sh - Compare file lists between two archive versions
OLD=$1
NEW=$2
echo "Files only in $OLD:"
comm -23 \
<(blob ls -o json "$OLD" | jq -r '.[].path' | sort) \
<(blob ls -o json "$NEW" | jq -r '.[].path' | sort)
echo ""
echo "Files only in $NEW:"
comm -13 \
<(blob ls -o json "$OLD" | jq -r '.[].path' | sort) \
<(blob ls -o json "$NEW" | jq -r '.[].path' | sort)
Usage:
./diff-archives.sh ghcr.io/myorg/archive:v1.0.0 ghcr.io/myorg/archive:v1.1.0
See Also
- CLI Reference - Complete command documentation
- CLI Getting Started - Tutorial introduction
- Provenance & Signing - Detailed signing and verification concepts
- Caching - Cache architecture and configuration