<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Getting Started with Pinner - Secure Your CI/CD Workflows</title>
<meta name="description" content="Step-by-step guide to installing and running Pinner. Secure your pipelines by translating mutable GitHub Actions and OCI Docker images into cryptographic digests.">
<meta name="keywords" content="install pinner, verify, hash-pinning setup, github actions verification, cargo installation">
<link rel="canonical" href="https://ffalcinelli.github.io/pinner/getting-started.html">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<meta property="og:type" content="website">
<meta property="og:url" content="https://ffalcinelli.github.io/pinner/getting-started.html">
<meta property="og:title" content="Getting Started with Pinner - Step-by-Step Guide">
<meta property="og:description" content="Set up Pinner in minutes. Follow this guide to install, initialize, and audit your CI/CD workflows using secure commit SHAs.">
<meta property="og:image" content="https://ffalcinelli.github.io/pinner/favicon.svg">
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="css/style.css">
<script src="js/components.js" defer></script>
</head>
<body class="font-sans antialiased overflow-x-hidden">
<div class="hero-glow"></div>
<pinner-navbar active-page="getting-started"></pinner-navbar>
<section class="relative pt-16 pb-12 hero-gradient border-b border-gray-800/40">
<div class="max-w-4xl mx-auto px-4">
<h1 class="text-4xl md:text-5xl font-extrabold text-white mb-6">
Getting <span class="text-sky-400">Started</span>
</h1>
<p class="text-xl text-gray-400 leading-relaxed">
Secure your CI/CD pipelines in minutes with this comprehensive guide to Pinner.
</p>
</div>
</section>
<main class="max-w-4xl mx-auto px-4 py-16 relative z-10">
<div id="copy-feedback" class="fixed top-8 left-1/2 -translate-x-1/2 px-4 py-2 bg-sky-500 text-white text-xs font-bold rounded shadow-lg opacity-0 pointer-events-none transition-all duration-300 z-50">
Copied to clipboard!
</div>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">1</span>
Installation
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">Choose the installation method that best fits your environment.</p>
<div class="space-y-6">
<div class="card p-6 rounded-xl">
<h3 class="text-lg font-bold text-white mb-4">Shell (macOS & Linux)</h3>
<div class="terminal p-1 flex items-center justify-between bg-black/40 border border-gray-800">
<code class="px-4 py-3 font-mono text-emerald-400 text-sm flex-1 break-all">curl -LsSf https://raw.githubusercontent.com/ffalcinelli/pinner/main/install.sh | sh</code>
<button onclick="window.copyCmd('curl -LsSf https://raw.githubusercontent.com/ffalcinelli/pinner/main/install.sh | sh', this)" class="px-4 py-3 hover:bg-emerald-400/10 rounded-md transition-all text-gray-400 hover:text-emerald-400 flex-shrink-0 group" title="Copy to clipboard">
<i class="far fa-copy group-active:scale-90 transition-transform"></i>
</button>
</div>
</div>
<div class="card p-6 rounded-xl">
<h3 class="text-lg font-bold text-white mb-4">PowerShell (Windows)</h3>
<div class="terminal p-1 flex items-center justify-between bg-black/40 border border-gray-800">
<code class="px-4 py-3 font-mono text-emerald-400 text-sm flex-1 break-all">powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/ffalcinelli/pinner/main/install.ps1 | iex"</code>
<button onclick="window.copyCmd('powershell -ExecutionPolicy ByPass -c \"irm https://raw.githubusercontent.com/ffalcinelli/pinner/main/install.ps1 | iex\"', this)" class="px-4 py-3 hover:bg-emerald-400/10 rounded-md transition-all text-gray-400 hover:text-emerald-400 flex-shrink-0 group" title="Copy to clipboard">
<i class="far fa-copy group-active:scale-90 transition-transform"></i>
</button>
</div>
</div>
<div class="card p-6 rounded-xl">
<h3 class="text-lg font-bold text-white mb-4">Cargo (Rust)</h3>
<div class="terminal p-1 flex items-center justify-between bg-black/40 border border-gray-800">
<code class="px-4 py-3 font-mono text-emerald-400 text-sm flex-1 break-all">cargo install pinner</code>
<button onclick="window.copyCmd('cargo install pinner', this)" class="px-4 py-3 hover:bg-emerald-400/10 rounded-md transition-all text-gray-400 hover:text-emerald-400 flex-shrink-0 group" title="Copy to clipboard">
<i class="far fa-copy group-active:scale-90 transition-transform"></i>
</button>
</div>
</div>
</div>
</section>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">2</span>
Pin Your First Workflow
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">
Navigate to your repository and run the <code class="text-sky-300">pin</code> command.
By default, Pinner looks for files in <code class="text-gray-300">.github/workflows/</code>.
</p>
<div class="terminal rounded-xl overflow-hidden border border-gray-800">
<div class="p-6 font-mono text-sm md:text-base leading-relaxed">
<p class="mb-2 text-gray-500">$ pinner pin</p>
<p class="mb-2 text-emerald-400">Searching for workflows in .github/workflows/...</p>
<p class="mb-2 text-white">Found 2 workflow files.</p>
<p class="mb-2 text-white"><span class="text-sky-400">actions/checkout@v4</span> -> <span class="text-sky-400">actions/checkout@8f4b7f84...</span> <span class="text-gray-500"># v4</span></p>
<p class="text-emerald-400 font-bold mt-4">Successfully pinned 3 actions! ✅</p>
</div>
</div>
<p class="text-gray-400 mt-6 italic">
<i class="fas fa-info-circle mr-2 text-sky-400"></i> Tip: Use <code class="text-sky-300">--dry-run</code> to see changes without writing to files.
</p>
</section>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">3</span>
Security Scan & Vetting
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">
Audit your dependencies for vulnerabilities. Pinner queries the OpenSSF OSV database for both current pinned hashes and proposed upgrade candidates. It also executes Sigstore/Cosign signature/provenance verification on container images.
Run the <code class="text-sky-300">scan</code> command to inspect your dependencies:
</p>
<div class="terminal rounded-xl overflow-hidden border border-gray-800">
<div class="p-6 font-mono text-sm md:text-base leading-relaxed">
<p class="mb-2 text-gray-500">$ pinner scan</p>
<p class="mb-2 text-emerald-400">Scanning dependencies with OSV database...</p>
<p class="mb-2 text-emerald-400">✔ Clean Dependencies:</p>
<p class="mb-2 text-white"> actions/checkout@8f4b7f84... (Upgrade candidate: df4cb1c0... # v6.0.3)</p>
<p class="mb-2 text-white"> alpine@sha256:12345... (Upgrade candidate: sha256:67890...)</p>
<p class="mb-2 text-emerald-400 font-bold mt-4">Select clean dependencies to add to the vetted whitelist in .pinner.toml: [actions/checkout@8f4b7f84..., actions/checkout@df4cb1c0...]</p>
</div>
</div>
<p class="text-gray-400 mt-6 leading-relaxed">
This adds vetted hashes to your whitelist in <code class="text-gray-300">.pinner.toml</code>. When proposing upgrades or pinning, Pinner displays visual feedback (e.g. <span class="text-emerald-400 font-bold">[✓ vetted]</span>) to ensure supply-chain integrity.
</p>
</section>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">4</span>
Supported Platforms & Forges
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">Pinner is designed for multi-forge environments and enterprise setups.</p>
<div class="overflow-x-auto rounded-xl border border-gray-800 card">
<table class="min-w-full text-left text-sm">
<thead class="bg-gray-800/50 text-white border-b border-gray-800">
<tr>
<th class="px-6 py-4 font-bold">Forge / Registry</th>
<th class="px-6 py-4 font-bold">API Environment Variable</th>
<th class="px-6 py-4 font-bold">Default URL</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800 text-gray-400">
<tr>
<td class="px-6 py-4 text-white font-medium">GitHub</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">GITHUB_TOKEN</code></td>
<td class="px-6 py-4">https://api.github.com</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">GitLab</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">GITLAB_TOKEN</code></td>
<td class="px-6 py-4">https://gitlab.com</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">Bitbucket</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">BITBUCKET_TOKEN</code></td>
<td class="px-6 py-4">https://api.bitbucket.org/2.0</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">Forgejo/Gitea</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">FORGEJO_TOKEN</code></td>
<td class="px-6 py-4">https://codeberg.org</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">Azure DevOps</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">GITHUB_TOKEN</code></td>
<td class="px-6 py-4">Monorepo Mapping</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">AWS ECR</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">PINNER_OCI_PASSWORD</code></td>
<td class="px-6 py-4">Private Registries</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">CircleCI</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">CIRCLECI_TOKEN</code></td>
<td class="px-6 py-4">Docker Image Pinning</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">Tekton Pipelines</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">-</code></td>
<td class="px-6 py-4">OCI Bundle Pinning (<code>bundle:</code> key)</td>
</tr>
<tr>
<td class="px-6 py-4 text-white font-medium">Kubernetes</td>
<td class="px-6 py-4"><code class="text-sky-300 text-xs">PINNER_OCI_PASSWORD</code></td>
<td class="px-6 py-4">Container Image Pinning (<code>image:</code> key)</td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">5</span>
Configuration File
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">
Create a <code class="text-gray-300">.pinner.toml</code> file in your repository root to customize behavior globally and share with your team.
</p>
<div class="card p-6 rounded-xl">
<pre class="text-xs md:text-sm text-gray-300"><code># Actions to exclude from processing
ignore = ["actions/checkout", "my-org/private-repo"]
# Concurrency and Caching settings
concurrency = 5
no_cache = false
cache_ttl = 7200 # Persistent caching duration in seconds (2h)
# API URL overrides (for Enterprise instances)
github_url = "https://github.mycompany.com/api/v3"
gitlab_url = "https://gitlab.mycompany.com/api/v4"
# Whitelist of vetted dependency references (compact inline array)
vetted = [
"actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332",
{ ref = "actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10", tag = "v6.0.3", timestamp = "2026-06-18T15:28:25Z" }
]</code></pre>
</div>
</section>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">6</span>
Verify in CI
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">
Prevent unpinned actions from being merged into your codebase by adding Pinner to your CI pipeline. You can enable vulnerability checks using <code>check-osv</code> and require explicit vetting with <code>strict</code> mode:
</p>
<div class="card p-6 rounded-xl mb-6">
<pre class="text-xs md:text-sm text-gray-300"><code>name: Pinning Check
on: [pull_request]
jobs:
verify-pinning:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v4
- name: Verify Pinning & Audit OSV
uses: ffalcinelli/pinner/action@main
with:
command: 'verify'
check-osv: true
strict: true</code></pre>
</div>
<div class="p-5 mb-8 bg-sky-500/5 border border-sky-500/20 rounded-xl flex items-start space-x-4">
<div class="flex-shrink-0 mt-1 text-sky-400">
<i class="fas fa-rotate text-xl animate-spin" style="animation-duration: 4s;"></i>
</div>
<div>
<h4 class="text-sm font-bold text-white mb-1.5 flex items-center">
Pinnerception Warning <span class="ml-2 px-1.5 py-0.5 bg-sky-500/10 text-sky-400 text-[9px] rounded uppercase tracking-wider">Recursive</span>
</h4>
<p class="text-xs text-gray-400 leading-relaxed">
Remember to pin the pinner! Trusting a security tool to verify your pinned dependencies using a mutable tag is like hiring a security guard who leaves the keys under the doormat. If we didn't pin the pinner, who would pin the pinner's pinners?
</p>
</div>
</div>
<p class="text-gray-400 mb-6 leading-relaxed">
Alternatively, you can install the CLI tool directly:
</p>
<div class="card p-6 rounded-xl">
<pre class="text-xs md:text-sm text-gray-300"><code>name: Pinning Check
on: [pull_request]
jobs:
verify-pinning:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v4
- name: Install Pinner
run: curl -LsSf https://raw.githubusercontent.com/ffalcinelli/pinner/main/install.sh | sh
- name: Verify Pinning
run: pinner verify --check-osv --strict</code></pre>
</div>
</section>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">7</span>
Shell Completion
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">
Generate tab-completion scripts. If you omit the shell argument, Pinner will attempt to detect your current shell automatically:
</p>
<div class="space-y-4">
<div class="card p-5 rounded-xl border-l-4 border-emerald-500">
<h3 class="text-sm font-bold text-white mb-2 uppercase tracking-widest opacity-60">Auto-Detection</h3>
<code class="text-emerald-400 text-xs md:text-sm">pinner generate-completion > ~/.pinner_completion</code>
</div>
<div class="card p-5 rounded-xl border-l-4 border-sky-500">
<h3 class="text-sm font-bold text-white mb-2 uppercase tracking-widest opacity-60">Bash (Explicit)</h3>
<code class="text-emerald-400 text-xs md:text-sm">pinner generate-completion bash > /etc/bash_completion.d/pinner</code>
</div>
<div class="card p-5 rounded-xl border-l-4 border-purple-500">
<h3 class="text-sm font-bold text-white mb-2 uppercase tracking-widest opacity-60">Zsh (Explicit)</h3>
<code class="text-emerald-400 text-xs md:text-sm">pinner generate-completion zsh > "${fpath[1]}/_pinner"</code>
</div>
</div>
</section>
<section class="mb-16">
<h2 class="text-2xl font-bold text-white mb-8 flex items-center">
<span class="w-8 h-8 bg-sky-500/20 text-sky-400 rounded-lg flex items-center justify-center mr-4 text-sm font-bold">8</span>
Automated Pull/Merge Requests
</h2>
<p class="text-gray-400 mb-6 leading-relaxed">
Pinner can automatically run pinning, check for changes, commit to a new git branch, push to origin, and create a Pull Request (GitHub) or Merge Request (GitLab) in a single step using configured API tokens:
</p>
<div class="terminal rounded-xl overflow-hidden border border-gray-800 mb-6">
<div class="p-6 font-mono text-sm md:text-base leading-relaxed">
<p class="mb-2 text-gray-500">$ GITHUB_TOKEN=ghp_xxx pinner pr-create --branch pinner/updates --message "security: pin dependencies"</p>
<p class="mb-2 text-emerald-400">Searching for workflows in .github/workflows/...</p>
<p class="mb-2 text-white">actions/checkout@v4 -> actions/checkout@8f4b7f84... # v4</p>
<p class="mb-2 text-emerald-400">Pushing branch pinner/updates to origin...</p>
<p class="mb-2 text-emerald-400">Sending Pull Request creation request to GitHub API...</p>
<p class="text-emerald-400 font-bold mt-2">GitHub Pull Request successfully created! 🚀</p>
</div>
</div>
</section>
<div class="p-8 bg-sky-500/5 border border-sky-500/10 rounded-2xl">
<h2 class="text-xl font-bold text-white mb-4">Next Steps</h2>
<ul class="space-y-4">
<li class="flex items-start">
<i class="fas fa-check-circle text-sky-400 mt-1 mr-3"></i>
<span class="text-gray-400">
<strong class="text-white">Learn about Upgrades:</strong> Use <code class="text-sky-300">pinner upgrade</code> to move to newer versions.
<p class="mt-2 text-xs text-amber-400/80 bg-amber-400/5 p-2 rounded border border-amber-400/10">
<i class="fas fa-exclamation-triangle mr-1"></i>
<strong>Security Note:</strong> Automatic upgrades can undermine security. Use <code>upgrade</code> as an intentional step followed by a manual check of changes.
</p>
</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle text-sky-400 mt-1 mr-3"></i>
<span class="text-gray-400"><strong class="text-white">API Reference:</strong> Check out the <a href="https://docs.rs/pinner" class="text-sky-400 hover:underline">Rust API documentation</a> for library usage details.</span>
</li>
</ul>
</div>
</main>
<pinner-footer></pinner-footer>
</body>
</html>