<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pinner - Secure CI/CD Workflows | Fast Dependency Pinning & Security Scanner</title>
<meta name="description" content="Secure your CI/CD pipelines against supply chain attacks. Pinner is a high-performance Rust CLI that pins mutable workflow actions and container image tags to immutable commit SHA-1 or digest hashes.">
<meta name="keywords" content="github-actions, gitlab-ci, bitbucket-pipelines, forgejo, docker, security scanner, dependency pinning, hash-pinning, supply chain security, cosign, sigstore, osv, cache">
<meta name="author" content="Fabio Falcinelli">
<link rel="canonical" href="https://ffalcinelli.github.io/pinner/index.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/index.html">
<meta property="og:title" content="Pinner - Secure CI/CD Workflows with Immutable Hashes">
<meta property="og:description" content="Protect workflows and Docker images against supply chain attacks. Auto-resolve tags to commit SHAs with Sigstore verification, OSV vulnerability scanning, and caching.">
<meta property="og:image" content="https://ffalcinelli.github.io/pinner/favicon.svg">
<meta property="twitter:card" content="summary">
<meta property="twitter:url" content="https://ffalcinelli.github.io/pinner/index.html">
<meta property="twitter:title" content="Pinner - Secure CI/CD Workflows">
<meta property="twitter:description" content="Secure your CI/CD pipelines. Auto-resolve actions and container tags to immutable SHA hashes with OSV security auditing.">
<meta property="twitter:image" content="https://ffalcinelli.github.io/pinner/favicon.svg">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Pinner",
"description": "Secure CI/CD workflows by pinning mutable tags to immutable SHA-1 hashes. Supports GitHub, GitLab, Bitbucket, Forgejo, OCI registries, OSV scanning, and OCI image signatures.",
"operatingSystem": "Linux, macOS, Windows",
"applicationCategory": "DeveloperApplication",
"license": "https://opensource.org/licenses/MIT",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"publishingPrinciples": "https://github.com/ffalcinelli/pinner",
"author": {
"@type": "Person",
"name": "Fabio Falcinelli"
}
}
</script>
<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="home"></pinner-navbar>
<section class="relative pt-20 pb-32 hero-gradient">
<div class="max-w-4xl mx-auto text-center px-4">
<h1 class="text-5xl md:text-7xl font-extrabold text-white mb-6 leading-tight">
Secure your CI/CD workflows with <span class="text-sky-400">Pinner</span>
</h1>
<p class="text-xl md:text-2xl text-gray-400 mb-8 max-w-3xl mx-auto leading-relaxed">
Automatically pin mutable tags to immutable SHA-1 hashes or OCI digests. Stop tag-jacking and supply chain attacks in milliseconds.
</p>
<div class="max-w-2xl mx-auto mb-16 origin-quote py-6 px-4 md:px-8 text-left border-l-4 border-sky-500/30 rounded-r-2xl bg-sky-500/[0.02]">
<div class="flex items-start space-x-6">
<div class="flex-shrink-0 mt-1">
<i class="fas fa-flask text-sky-400/60 text-3xl"></i>
</div>
<div>
<h3 class="text-white font-bold mb-2 flex items-center">
The Pinner Reaction <span class="ml-3 px-2 py-0.5 bg-sky-500/10 text-sky-400 text-[10px] rounded uppercase tracking-widest">Etymology</span>
</h3>
<p class="text-gray-400 text-sm leading-relaxed mb-4">
In organic chemistry, the <span class="text-sky-300 font-medium">Pinner reaction</span> involves the acid-catalyzed conversion of a reactive nitrile into a highly stable salt.
</p>
<p class="text-gray-300 italic text-base leading-relaxed border-t border-gray-800/40 pt-4">
"Just as the chemical reaction transforms a volatile compound into a stable, fixed salt, this CLI transforms floating tags into secure, immutable commit SHAs."
</p>
</div>
</div>
</div>
<div class="flex flex-col md:flex-row items-center justify-center gap-4 mb-16 relative z-10">
<a href="getting-started.html" class="w-full md:w-auto px-8 py-4 btn-primary text-white font-bold rounded-lg transition-all hover-scale shadow-lg shadow-sky-500/20 text-center">
Get Started
</a>
<a href="https://github.com/ffalcinelli/pinner" class="w-full md:w-auto px-8 py-4 btn-secondary text-white font-bold rounded-lg transition-all hover-scale border border-gray-700 text-center">
View on GitHub
</a>
</div>
<div class="terminal rounded-xl overflow-hidden shadow-2xl text-left border border-gray-800/80">
<div class="bg-gray-800/40 px-4 py-2.5 flex items-center justify-between border-b border-gray-800/50">
<div class="flex items-center space-x-2">
<div class="w-3 h-3 bg-red-500/80 rounded-full"></div>
<div class="w-3 h-3 bg-yellow-500/80 rounded-full"></div>
<div class="w-3 h-3 bg-green-500/80 rounded-full"></div>
</div>
<span class="text-[10px] text-gray-500 uppercase tracking-widest font-bold">Terminal - Verification & Security Auditing</span>
</div>
<div class="p-6 font-mono text-sm md:text-base leading-relaxed">
<p class="mb-2 text-gray-500">$ pinner pin --cache-ttl 7200</p>
<p class="mb-2 text-emerald-400">Searching for workflows in .github/workflows/...</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> <span class="text-emerald-400 font-bold">[✓ vetted]</span></p>
<p class="mb-2 text-white"><span class="text-sky-400">dtolnay/rust-toolchain@master</span> -> <span class="text-sky-400">dtolnay/rust-toolchain@e97e2d8c...</span> <span class="text-gray-500"># master</span> <span class="text-yellow-400">[? not checked]</span></p>
<p class="text-emerald-400 font-bold mt-4 mb-6">Successfully pinned 12 actions across 3 files! [Cache updated - TTL 2h]</p>
<p class="mb-2 text-gray-500">$ pinner verify --check-osv --strict</p>
<p class="mb-2 text-emerald-400">Verifying dependencies against configuration and OSV database...</p>
<p class="mb-2 text-emerald-400">✔ actions/checkout@8f4b7f84... (No known vulnerabilities)</p>
<p class="mb-2 text-emerald-400">✔ dtolnay/rust-toolchain@e97e2d8c... (Vetted locally)</p>
<p class="text-emerald-400 font-bold mt-2">All scanned dependencies are secure and properly pinned! ✅</p>
</div>
</div>
</div>
</section>
<section class="py-24 max-w-7xl mx-auto px-4 md:px-8 relative z-10" id="features">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
<div class="card p-8 rounded-2xl hover:border-sky-500/50">
<div class="w-12 h-12 bg-sky-500/10 rounded-lg flex items-center justify-center mb-6">
<i class="fas fa-bolt text-sky-400 text-xl"></i>
</div>
<h3 class="text-xl font-bold text-white mb-4">High Performance</h3>
<p class="text-gray-400 leading-relaxed text-sm">
Written in Rust with AST parsing via <code>tree-sitter</code>. Optimized with concurrent resolving and persistent disk caching (<code>cacache</code>) to scan hundreds of files in milliseconds.
</p>
</div>
<div class="card p-8 rounded-2xl hover:border-emerald-500/50">
<div class="w-12 h-12 bg-emerald-500/10 rounded-lg flex items-center justify-center mb-6">
<i class="fas fa-shield-halved text-emerald-400 text-xl"></i>
</div>
<h3 class="text-xl font-bold text-white mb-4">Cosign Provenance</h3>
<p class="text-gray-400 leading-relaxed text-sm">
Supports OCI registry image pinning, Docker credential lookups via desktop helpers, and performs automated structural Sigstore/Cosign provenance and signature checks.
</p>
</div>
<div class="card p-8 rounded-2xl hover:border-red-500/50">
<div class="w-12 h-12 bg-red-500/10 rounded-lg flex items-center justify-center mb-6">
<i class="fas fa-file-shield text-red-400 text-xl"></i>
</div>
<h3 class="text-xl font-bold text-white mb-4">OSV Database Auditing</h3>
<p class="text-gray-400 leading-relaxed text-sm">
Direct integration with the OpenSSF OSV vulnerability database. Scans and filters dependencies for vulnerabilities, and fails verification if compromised hashes are detected.
</p>
</div>
<div class="card p-8 rounded-2xl hover:border-purple-500/50">
<div class="w-12 h-12 bg-purple-500/10 rounded-lg flex items-center justify-center mb-6">
<i class="fas fa-sliders text-purple-400 text-xl"></i>
</div>
<h3 class="text-xl font-bold text-white mb-4">Enterprise Controls</h3>
<p class="text-gray-400 leading-relaxed text-sm">
Configure trust whitelists, local policy overrides, and rate-limits in <code>.pinner.toml</code>. Supports private registries, AWS ECR, Azure DevOps, and custom git forges.
</p>
</div>
</div>
</section>
<section class="py-24 bg-gray-900/10 border-y border-gray-800/40 relative z-10">
<div class="max-w-4xl mx-auto px-4 md:px-8 text-center">
<h2 class="text-3xl font-bold text-white mb-12 flex items-center justify-center">
<i class="fas fa-download text-sky-400 mr-4"></i> Install Pinner
</h2>
<div class="flex justify-center mb-8">
<div class="flex flex-col sm:flex-row w-full sm:w-auto p-1 bg-gray-800/30 border border-gray-800/60 rounded-xl space-y-1 sm:space-y-0 sm:space-x-1">
<button onclick="switchTab('linux')" id="tab-linux" class="flex items-center justify-center w-full px-6 py-2.5 text-xs font-bold uppercase tracking-widest transition-all rounded-lg text-gray-400 hover:text-gray-200">
<i class="fab fa-linux mr-2"></i><i class="fab fa-apple mr-2"></i> Linux / macOS
</button>
<button onclick="switchTab('windows')" id="tab-windows" class="flex items-center justify-center w-full px-6 py-2.5 text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-gray-200 transition-all rounded-lg">
<i class="fab fa-windows mr-2"></i> Windows
</button>
<button onclick="switchTab('cargo')" id="tab-cargo" class="flex items-center justify-center w-full px-6 py-2.5 text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-gray-200 transition-all rounded-lg">
<i class="fas fa-box-open mr-2"></i> Cargo
</button>
</div>
</div>
<div class="relative max-w-2xl mx-auto">
<div id="cmd-linux" class="terminal rounded-lg p-1 flex items-center justify-between bg-black/40 border border-gray-800 shadow-xl">
<code class="px-6 py-4 font-mono text-emerald-400 text-sm md:text-base 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-6 py-4 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 id="cmd-windows" class="terminal rounded-lg p-1 hidden items-center justify-between bg-black/40 border border-gray-800 shadow-xl">
<code class="px-6 py-4 font-mono text-emerald-400 text-sm md:text-base 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-6 py-4 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 id="cmd-cargo" class="terminal rounded-lg p-1 hidden items-center justify-between bg-black/40 border border-gray-800 shadow-xl">
<code class="px-6 py-4 font-mono text-emerald-400 text-sm md:text-base flex-1 break-all">cargo install pinner</code>
<button onclick="window.copyCmd('cargo install pinner', this)" class="px-6 py-4 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 id="copy-feedback" class="absolute -top-12 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">
Copied to clipboard!
</div>
</div>
<p class="mt-8 text-sm text-gray-500 font-medium">One-line installation for instant workflow security.</p>
</div>
</section>
<section class="py-16 max-w-4xl mx-auto px-4 relative z-10">
<div class="relative overflow-hidden rounded-2xl border border-sky-500/10 bg-sky-500/[0.01] p-8 md:p-12 shadow-xl card">
<div class="absolute -right-12 -bottom-12 opacity-[0.02] pointer-events-none select-none">
<i class="fas fa-rotate text-sky-400 text-[180px] animate-spin" style="animation-duration: 20s;"></i>
</div>
<div class="relative z-10 flex flex-col md:flex-row items-center md:items-start md:space-x-8 text-center md:text-left">
<div class="flex-shrink-0 p-4 bg-sky-500/10 rounded-full mb-6 md:mb-0">
<i class="fas fa-rotate text-sky-400 text-3xl animate-spin" style="animation-duration: 6s;"></i>
</div>
<div>
<h3 class="text-xl md:text-2xl font-extrabold text-white mb-3 flex items-center justify-center md:justify-start">
Pinnerception: The Recursive Paradox 🌀
</h3>
<p class="text-gray-400 text-sm md:text-base leading-relaxed mb-4">
Remember to pin the pinner! If you run the Pinner Action in your CI pipelines, you should hash-pin <strong>Pinner</strong> using Pinner itself.
</p>
<div class="border-l-2 border-sky-500/40 pl-4 py-1.5 mb-4 italic text-gray-300 text-sm leading-relaxed">
"Trusting a security action to verify your pinned dependencies using a mutable tag is like hiring a security guard who leaves the keys under the doormat."
</div>
<p class="text-xs md:text-sm text-sky-300 font-medium">
Remember: If you don't pin the pinner, who pins the pinner's pinners? Mind the recursion.
</p>
</div>
</div>
</div>
</section>
<pinner-footer></pinner-footer>
<script>
function switchTab(os) {
['linux', 'windows', 'cargo'].forEach(id => {
const tab = document.getElementById('tab-' + id);
const cmd = document.getElementById('cmd-' + id);
if (id === os) {
tab.classList.add('tab-active');
tab.classList.remove('text-gray-400');
cmd.classList.remove('hidden');
cmd.classList.add('flex');
} else {
tab.classList.remove('tab-active');
tab.classList.add('text-gray-400');
cmd.classList.add('hidden');
cmd.classList.remove('flex');
}
});
}
window.onload = () => {
const platform = window.navigator.platform.toLowerCase();
if (platform.includes('win')) {
switchTab('windows');
} else {
switchTab('linux');
}
};
</script>
</body>
</html>