containers/skopeo
Work with remote images registries - retrieving information, images, signing content
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓10 active contributors
- ✓Apache-2.0 licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 58% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/containers/skopeo)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/containers/skopeo on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: containers/skopeo
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/containers/skopeo shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 10 active contributors
- Apache-2.0 licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 58% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live containers/skopeo
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/containers/skopeo.
What it runs against: a local clone of containers/skopeo — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in containers/skopeo | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of containers/skopeo. If you don't
# have one yet, run these first:
#
# git clone https://github.com/containers/skopeo.git
# cd skopeo
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of containers/skopeo and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "containers/skopeo(\\.git)?\\b" \\
&& ok "origin remote is containers/skopeo" \\
|| miss "origin remote is not containers/skopeo (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "cmd/skopeo/main.go" \\
&& ok "cmd/skopeo/main.go" \\
|| miss "missing critical file: cmd/skopeo/main.go"
test -f "cmd/skopeo/copy.go" \\
&& ok "cmd/skopeo/copy.go" \\
|| miss "missing critical file: cmd/skopeo/copy.go"
test -f "go.mod" \\
&& ok "go.mod" \\
|| miss "missing critical file: go.mod"
test -f "cmd/skopeo/utils.go" \\
&& ok "cmd/skopeo/utils.go" \\
|| miss "missing critical file: cmd/skopeo/utils.go"
test -f "Makefile" \\
&& ok "Makefile" \\
|| miss "missing critical file: Makefile"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/containers/skopeo"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Skopeo is a command-line utility for performing operations on container images and registries without requiring root or a daemon. It copies images between registries (docker.io, quay.io, private registries, local OCI-layout directories), inspects remote images to show layer details, deletes images, syncs external registries for air-gapped deployments, and handles signing/authentication—all via the Docker Registry HTTP API V2 and OCI image specifications. Monolithic CLI tool: cmd/skopeo/main.go orchestrates command entry points (copy.go, inspect.go, delete.go, sync.go, login.go, manifest.go, layers.go, signing.go, proxy.go, etc.). cmd/skopeo/inspect/ subdirectory contains output formatting logic. Test files (*_test.go) colocate with command implementations. cmd/skopeo/fixtures/ holds test data (manifests, GPG keys, signatures). No internal packages directory—logic is in cmd.
👥Who it's for
Container platform engineers, DevOps practitioners, and registry operators who need to move, inspect, and manage container images across heterogeneous registries (Docker registries, Podman storage backends, OCI directories) without pulling entire images locally or running privileged operations.
🌱Maturity & risk
Production-ready and actively developed. The project has clear governance (GOVERNANCE.md, MAINTAINERS.md), OpenSSF Best Practices badge, comprehensive CI via Cirrus (.cirrus.yml) and GitHub Actions (.github/workflows/), security policy (SECURITY.md), and a release cadence on GitHub. Go module version is 1.25.6 with up-to-date dependencies (go.podman.io packages from 2026).
Low to moderate risk. Dependencies are primarily from trusted container ecosystem projects (go.podman.io/image, go.podman.io/storage, opencontainers/image-spec) with regular updates. No single-maintainer signals visible. Main risk: heavy reliance on OpenContainers standards (image-spec v1.1.2) and Podman ecosystem libraries—breaking changes upstream could require coordinated updates across the stack.
Active areas of work
Active maintenance with CI/CD automation: .cirrus.yml orchestrates testing across platforms, .packit.yaml handles downstream packaging, .github/workflows manage issue locking and staleness. Recent dependency updates via renovate.json5. Project follows standard Go patterns with golangci.yml linting configuration. No specific breaking changes visible in file list, but versioning via Masterminds/semver indicates semantic versioning practices.
🚀Get running
git clone https://github.com/containers/skopeo && cd skopeo && make && make install. Requires Go 1.25.6+ per go.mod. Makefile (top-level) provides targets for building and installation. No npm/yarn—pure Go build toolchain.
Daily commands: make (builds binary to ./skopeo) or make install (installs to system PATH). For testing: go test ./cmd/skopeo/... or make test (inferred from standard Go patterns). Run commands like: ./skopeo copy docker://source/image:tag docker://dest/image:tag, ./skopeo inspect docker://registry/image:tag, ./skopeo login -u user -p pass docker.io.
🗺️Map of the codebase
cmd/skopeo/main.go— Entry point for the CLI application; defines command structure and initialization logic that all contributors must understand.cmd/skopeo/copy.go— Core image copying functionality; the primary use case for skopeo and foundation for understanding the codebase's architecture.go.mod— Declares dependencies on go.podman.io/image and containers libraries that define the image handling abstractions throughout the codebase.cmd/skopeo/utils.go— Shared utility functions for authentication, registry interaction, and image reference parsing used across all commands.Makefile— Build, test, and installation targets that standardize the development workflow for all contributors.README.md— Defines the project scope, capabilities, and supported operations that frame all development decisions.cmd/skopeo/signing.go— Image signature and verification logic; critical security subsystem that must be understood for trust-related features.
🛠️How to make changes
Add a new skopeo subcommand
- Create a new file cmd/skopeo/mycommand.go with a function that returns a cobra.Command (
cmd/skopeo/mycommand.go) - Register the command in cmd/skopeo/main.go by adding rootCmd.AddCommand(newMyCommand()) in the init function (
cmd/skopeo/main.go) - Add utility functions as needed by reusing functions from cmd/skopeo/utils.go (e.g., getSystemContext, commandTimeoutContext) (
cmd/skopeo/utils.go) - Create cmd/skopeo/mycommand_test.go with unit tests using cobra's testing patterns (
cmd/skopeo/mycommand_test.go) - Create docs/skopeo-mycommand.1.md documenting the command, flags, and examples (
docs/skopeo-mycommand.1.md)
Add image verification with GPG signatures
- Review existing GPG signature logic in cmd/skopeo/signing.go to understand the pattern (
cmd/skopeo/signing.go) - Call verifyImageSignatures() from your command handler, passing the image reference and context (
cmd/skopeo/copy.go) - Load policy from default-policy.json or custom path using loadPolicy() utility (
cmd/skopeo/utils.go) - Configure GPG keyring paths via environment variables or CLI flags following copy.go pattern (
cmd/skopeo/copy.go)
Support a new registry type or transport
- Check go.podman.io/image dependency in go.mod to see supported image transport types (
go.mod) - Update cmd/skopeo/utils.go parseImageURL() to handle the new transport scheme if custom parsing is needed (
cmd/skopeo/utils.go) - Add SystemContext configuration in getSystemContext() to set transport-specific options (
cmd/skopeo/utils.go) - Add integration tests in integration/copy_test.go to validate the new transport end-to-end (
integration/copy_test.go)
Enhance authentication flow
- Review existing login/logout commands in cmd/skopeo/login.go and cmd/skopeo/logout.go (
cmd/skopeo/login.go) - Modify getAuthConfig() in cmd/skopeo/utils.go to support new credential sources (
cmd/skopeo/utils.go) - Add tests in cmd/skopeo/login_test.go and cmd/skopeo/logout_test.go to verify credential handling (
cmd/skopeo/login_test.go) - Update the policy or system context configuration to include new auth mechanisms (
cmd/skopeo/utils.go)
🔧Why these technologies
- Cobra (github.com/spf13/cobra) — Standard Go CLI framework provides flag parsing, subcommand routing, and help generation with minimal boilerplate.
- go.podman.io/image (containers OCI library) — Abstracts container registry protocols (Docker V2, OCI), local storage, and layer manipulation behind a unified interface.
- GPG (gnupg) + go.podman.io/signature — Provides cryptographic image signing and verification following Docker Content Trust standards for supply-chain security.
- containers/ocicrypt — Implements OCI Image Encryption Spec for encrypted container images at rest and in transit.
- Docker/distribution library — Legacy support for Docker V2 registry API and manifest format compatibility.
⚖️Trade-offs already made
-
No daemon required; operates in user space
- Why: Reduces operational complexity and eliminates privilege escalation risks for typical operations.
- Consequence: Cannot leverage shared image cache from container runtime; each pull/push is independent, potentially slower for repeated operations.
-
Single binary with all transports (docker://, oci://, file://) compiled in
- Why: Simplifies deployment and user experience; no plugin system or runtime dependencies.
- Consequence: Binary size is larger (~60MB); cannot selectively disable transports; transport-specific features are always present.
-
Signature verification via local GPG keyring or Sigstore, not integrated registry trust
- Why: Gives users explicit control over trust policies without depending on registry infrastructure.
- Consequence: Requires manual policy configuration (default-policy.json); signature verification is opt-in, not automatic.
-
Synchronous, blocking operations (no async queue or streaming output)
- Why: Predictable behavior, easier debugging, and simple progress reporting.
- Consequence: Large image copies block the CLI; no resume capability on network interruption; less suitable for CI/CD pipelines with strict timeout requirements.
🚫Non-goals (don't propose these)
- Does not store or cache images locally (except temporary during copy); skopeo is a transport tool, not a registry or storage backend.
- Does not build images; focuses on existing image transfer and inspection
🪤Traps & gotchas
Authentication state stored in $XDG_RUNTIME_DIR/containers/auth.json—if not set, defaults to ~/.config/containers/ (Linux). GPG signing requires pubring.gpg and secring.gpg in correct locations (see fixtures/ for test setup). Docker daemon transport (docker-daemon:) requires Docker socket access and proper daemon connectivity. OCI-layout operations require strict directory structure (see oci-image-spec). Proxy support platform-specific: cmd/skopeo/proxy_windows.go suggests Windows-only implementation. Tests in *_test.go files may require network access or mock registries—check test fixtures. Cross-registry copy may silently fail if auth.json credentials are missing or expired.
🏗️Architecture
💡Concepts to learn
- Docker Registry HTTP API V2 — Skopeo's core protocol for interacting with registries; understanding request/response patterns for manifest pulls, blob uploads, and authentication is essential for debugging registry operations
- OCI Image Format (image-spec) — Skopeo operates on OCI-compliant images (config.json, layers, manifests); understanding image layout is critical for copy, inspect, and oci:// transport operations
- Transport Abstraction Layer — Skopeo abstracts multiple storage backends (docker://, docker-archive:, dir:, oci:, containers-storage:) behind a unified interface; understanding transport plugins is needed to add new backends
- GPG Signing and Image Signatures — Skopeo supports Docker Content Trust via GPG signing (fixtures/pubring.gpg, secring.gpg); understanding signature creation/verification is needed for signing.go modifications
- Container Layer Compression and Digests — Layers are identified by SHA256 digests and may be gzipped; copy.go and inspect.go must handle digest verification and re-compression during transfers
- Credential Storage (auth.json) — Skopeo persists registry credentials in $XDG_RUNTIME_DIR/containers/auth.json; login.go/logout.go manage this file and understanding its structure is essential for auth debugging
- Image Manifest Versioning (v1/v2/OCI) — Skopeo handles Docker v2 manifests and OCI manifests with different schemas; inspect.go and manifest.go must parse and convert between formats (see fixtures/image.manifest.json for examples)
🔗Related repos
containers/image— Backend library for image transport abstraction (imported as go.podman.io/image v5) that Skopeo's copy/inspect commands depend on for registry operationscontainers/podman— Primary consumer of Skopeo; Podman uses containers/storage (go.podman.io/storage) backend that Skopeo also supports via containers-storage:// transportopencontainers/image-spec— OCI standard specification (v1.1.2) that Skopeo implements for manifest, config, and layer handling in oci:// and dir:// transportsdocker/distribution— Docker Registry V2 protocol reference implementation that Skopeo's docker:// transport implements to communicate with registriescontainers/buildah— Complementary container build tool that uses same containers/storage backend and shares auth.json credentials with Skopeo
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for cmd/skopeo/proxy.go and cmd/skopeo/proxy_windows.go
The proxy functionality has platform-specific implementations (proxy.go for Unix-like systems and proxy_windows.go for Windows) but there are no dedicated test files (proxy_test.go, proxy_windows_test.go) visible in the file structure. Given that skopeo is a critical tool for container registry operations, proxy configuration and authentication handling warrant comprehensive unit tests to ensure cross-platform reliability.
- [ ] Create cmd/skopeo/proxy_test.go with tests for proxy detection and configuration parsing
- [ ] Create cmd/skopeo/proxy_windows_test.go with Windows-specific proxy tests
- [ ] Test environment variable handling (HTTP_PROXY, HTTPS_PROXY, NO_PROXY)
- [ ] Test Windows-specific registry configuration reading
- [ ] Add tests for edge cases like malformed proxy URLs and authentication with proxies
Add integration tests for cmd/skopeo/layers.go operations
The layers.go command exists but there's no corresponding layers_test.go file. This is a critical feature for examining image layers, and the lack of tests means no automated validation of layer extraction, manifest parsing, or format output. This is especially important given the complexity of OCI and Docker v2 layer handling.
- [ ] Create cmd/skopeo/layers_test.go with test fixtures for multi-layer images
- [ ] Add tests for layer digest computation and verification
- [ ] Test layer output formatting (JSON, raw, etc.)
- [ ] Add tests for both OCI and Docker v2 image formats using fixtures from cmd/skopeo/fixtures/
- [ ] Test error cases like corrupted manifests and missing layer blobs
Create cmd/skopeo/delete_test.go with comprehensive delete operation tests
The delete.go command exists but lacks a corresponding test file (delete_test.go is not in the file structure). The delete operation is dangerous and irreversible, making comprehensive testing critical to prevent data loss. Tests should cover various registry types, authentication scenarios, and error conditions.
- [ ] Create cmd/skopeo/delete_test.go with tests for image deletion across different registry types
- [ ] Add tests for authenticated and unauthenticated registry deletion
- [ ] Test error handling for non-existent images and permission denied scenarios
- [ ] Add tests for tag deletion vs full image deletion
- [ ] Test with docker://, oci://, and containerd:// image sources from the various supported backends
🌿Good first issues
- Add integration tests for cmd/skopeo/proxy.go and cmd/skopeo/proxy_windows.go to verify proxy configuration works across Linux/Windows (currently missing *_test.go alongside proxy.go)
- Extend cmd/skopeo/inspect/output.go to support XML output format alongside existing JSON/YAML (inspect command lacks XML serialization compared to other container tools)
- Document TLS certificate validation flags and --insecure-policy behavior in CONTRIBUTING.md with concrete examples (copy_test.go has fixtures but usage docs sparse)
⭐Top contributors
Click to expand
Top contributors
- @mtrmac — 58 commits
- @renovate[bot] — 27 commits
- @lsm5 — 3 commits
- @baude — 3 commits
- @Luap99 — 3 commits
📝Recent commits
Click to expand
Recent commits
516159d— Merge pull request #2869 from containers/renovate/golangci-golangci-lint-2.x (mtrmac)c8831c8— Update dependency golangci/golangci-lint to v2.12.2 (renovate[bot])82a7169— Merge pull request #2867 from containers/renovate/golangci-golangci-lint-2.x (mtrmac)4df9e1d— Update dependency golangci/golangci-lint to v2.12.1 (renovate[bot])bf9d80d— Merge pull request #2868 from containers/renovate/common-image-and-storage-deps (mtrmac)0157bba— Update common, image, and storage deps to c03a490 (renovate[bot])b71619a— Merge pull request #2866 from containers/renovate/github.com-masterminds-semver-v3-3.x (mtrmac)bb107be— Update module github.com/Masterminds/semver/v3 to v3.5.0 (renovate[bot])35fcbb8— Merge pull request #2865 from lsm5/packit-downstream (mtrmac)277f715— Packit: Only create dist-git PRs for rawhide (lsm5)
🔒Security observations
The skopeo project demonstrates reasonable security practices with active maintenance and following container security best practices. However, several concerns exist: (1) the use of extremely new and uncommon Go version (1.25.6) with potential stability/patch issues; (2) reliance on development/pre-release versions of internal dependencies rather than stable releases; (3) cryptographic test fixtures stored in version control; (4) incomplete go.mod visibility preventing full dependency vulnerability assessment. The project would benefit from using stable dependency versions, implementing comprehensive vulnerability scanning in CI/CD, and enhancing supply chain security measures. The project has OpenSSF Best Practices certification, which is a positive indicator, but the identified issues should be addressed to improve overall security posture.
- Medium · Outdated Go Version Requirement —
go.mod. The project requires Go 1.25.6, which is extremely new and may not have sufficient security patches and community testing. This version is ahead of current stable releases, creating maintenance and compatibility risks. Fix: Use a stable, well-tested Go version (e.g., 1.23.x or 1.24.x) that has been in production use for sufficient time to identify and patch security issues. - Medium · Pinned Development Dependencies with Uncommon Versions —
go.mod (go.podman.io/common, go.podman.io/image/v5, go.podman.io/storage). Several dependencies use unusual version schemes (e.g., 'v1.1.2-0.20260226102121-a4c6ade7bb82', 'v5.39.3-0.20260504103427-c03a490dd857') that appear to be unreleased or development versions. This increases risk of using unmaintained or pre-release code with unpatched vulnerabilities. Fix: Switch to official stable releases. If pre-release versions are necessary, implement additional code review and security scanning processes. - Medium · Incomplete Dependency Analysis in go.mod —
go.mod. The go.mod file is truncated at 'github.com/docker/go-connections v0.7.0 //', making it impossible to verify all transitive dependencies for known vulnerabilities. Fix: Review the complete go.mod file and run 'go mod graph' and vulnerability scanners (e.g., 'govulncheck', 'trivy') to identify all transitive dependency vulnerabilities. - Low · Test Fixtures with Sensitive Data —
cmd/skopeo/fixtures/. The codebase includes GPG key fixtures (pubring.gpg, secring.gpg, trustdb.gpg) in the repository. While these appear to be test fixtures, storing cryptographic material in version control poses a risk if accidentally used in production or if the repository is compromised. Fix: Ensure these are clearly marked as test-only fixtures. Document that these should never be used in production. Consider adding pre-commit hooks to prevent accidental inclusion of real keys. - Low · Passphrase Fixture in Repository —
cmd/skopeo/fixtures/empty.passphrase. The file 'empty.passphrase' in fixtures could be accidentally confused with a real passphrase file, though it appears to be empty. Fix: Ensure clear documentation that this is a test fixture. Consider renaming to 'test_empty_passphrase' to make its purpose explicit. - Low · Missing SBOM/Supply Chain Security Verification —
Repository root / CI configuration. No evidence of SBOM generation, signed commits, or supply chain security measures in the provided file structure, despite the project working with container image registries where supply chain security is critical. Fix: Implement SBOM generation in the build pipeline, enable signed commits/tags, use SLSA framework attestations, and implement provenance verification.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.