A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
72
fork

Configure Feed

Select the types of activity you want to include in your feed.

Image Signing with ATProto#

ATCR provides cryptographic verification of container images through ATProto's native signature system. Every manifest stored in a PDS is cryptographically signed, providing tamper-proof image verification.

Overview#

Key Fact: Every image pushed to ATCR is automatically signed via ATProto's repository commit signing. No additional signing tools or steps are required.

When you push an image:

  1. Manifest stored in your PDS as an io.atcr.manifest record
  2. PDS signs the repository commit containing the manifest (ECDSA K-256)
  3. Signature is part of the ATProto repository chain
  4. Verification proves the manifest came from your DID and hasn't been tampered with

This document explains:

  • How ATProto signatures work for ATCR images
  • How to verify signatures using standard and custom tools
  • Integration options for different use cases
  • When to use optional X.509 certificates (Hold-as-CA)

ATProto Signature Model#

How It Works#

ATProto uses a repository commit signing model similar to Git:

1. docker push atcr.io/alice/myapp:latest
   ↓
2. AppView stores manifest in alice's PDS as io.atcr.manifest record
   ↓
3. PDS creates repository commit containing the new record
   ↓
4. PDS signs commit with alice's private key (ECDSA K-256)
   ↓
5. Commit becomes part of alice's cryptographically signed repo chain

What this proves:

  • ✅ Manifest came from alice's PDS (DID-based identity)
  • ✅ Manifest content hasn't been tampered with
  • ✅ Manifest was created at a specific time (commit timestamp)
  • ✅ Manifest is part of alice's verifiable repository history

Trust model:

  • Public keys distributed via DID documents (PLC directory, did:web)
  • Signatures use ECDSA K-256 (secp256k1)
  • Verification is decentralized (no central CA required)
  • Users control their own DIDs and can rotate keys

Signature Metadata#

In addition to ATProto's native commit signatures, ATCR creates ORAS signature artifacts that bridge ATProto signatures to the OCI ecosystem:

{
  "$type": "io.atcr.atproto.signature",
  "version": "1.0",
  "subject": {
    "digest": "sha256:abc123...",
    "mediaType": "application/vnd.oci.image.manifest.v1+json"
  },
  "atproto": {
    "did": "did:plc:alice123",
    "handle": "alice.bsky.social",
    "pdsEndpoint": "https://bsky.social",
    "recordUri": "at://did:plc:alice123/io.atcr.manifest/abc123",
    "commitCid": "bafyreih8...",
    "signedAt": "2025-10-31T12:34:56.789Z"
  },
  "signature": {
    "algorithm": "ECDSA-K256-SHA256",
    "keyId": "did:plc:alice123#atproto",
    "publicKeyMultibase": "zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDdo1Ko4Z"
  }
}

Stored as:

  • OCI artifact with artifactType: application/vnd.atproto.signature.v1+json
  • Linked to image manifest via OCI Referrers API
  • Discoverable by standard OCI tools (ORAS, Cosign, Crane)

Verification#

Quick Verification (Shell Script)#

For manual verification, use the provided shell scripts:

# Verify an image
./examples/verification/atcr-verify.sh atcr.io/alice/myapp:latest

# Output shows:
# - DID and handle of signer
# - PDS endpoint
# - ATProto record URI
# - Signature verification status

See: examples/verification/README.md for complete examples including:

  • Standalone verification script
  • Secure pull wrapper (verify before pull)
  • Kubernetes webhook deployment
  • CI/CD integration examples

Standard Tools (Discovery Only)#

Standard OCI tools can discover ATProto signature artifacts but cannot verify them (different signature format):

# Discover signatures with ORAS
oras discover atcr.io/alice/myapp:latest \
  --artifact-type application/vnd.atproto.signature.v1+json

# Fetch signature metadata
oras pull atcr.io/alice/myapp@sha256:sig789...

# View with Cosign (discovery only)
cosign tree atcr.io/alice/myapp:latest

Note: Cosign/Notary cannot verify ATProto signatures directly because they use a different signature format and trust model. Use integration plugins or the atcr-verify CLI tool instead.

Integration Options#

ATCR supports multiple integration approaches depending on your use case:

Build plugins for existing policy/verification engines:

Ratify Verifier Plugin:

  • Integrates with OPA Gatekeeper
  • Verifies ATProto signatures using Ratify's plugin interface
  • Policy-based enforcement for Kubernetes

OPA Gatekeeper External Provider:

  • HTTP service that verifies ATProto signatures
  • Rego policies call external provider
  • Flexible and easy to deploy

Containerd 2.0 Bindir Plugin:

  • Verifies signatures at containerd level
  • Works with any CRI-compatible runtime
  • No Kubernetes required

See: docs/SIGNATURE_INTEGRATION.md for complete plugin implementation examples

2. CLI Tool (atcr-verify)#

Standalone CLI tool for signature verification:

# Install
go install github.com/atcr-io/atcr/cmd/atcr-verify@latest

# Verify image
atcr-verify atcr.io/alice/myapp:latest --policy trust-policy.yaml

# Use in CI/CD
atcr-verify $IMAGE --quiet && kubectl apply -f deployment.yaml

Features:

  • Trust policy management (which DIDs to trust)
  • Multiple output formats (text, JSON, SARIF)
  • Offline verification with cached DID documents
  • Library usage for custom integrations

See: docs/ATCR_VERIFY_CLI.md for complete CLI specification

3. External Services#

Deploy verification as a service:

GitHub Actions:

- name: Verify image signature
  uses: atcr-io/atcr-verify-action@v1
  with:
    image: atcr.io/alice/myapp:${{ github.sha }}
    policy: .atcr/trust-policy.yaml

GitLab CI, Jenkins, CircleCI:

  • Use atcr-verify CLI in pipeline
  • Fail build if verification fails
  • Enforce signature requirements before deployment

4. X.509 Certificates (Hold-as-CA) ⚠️#

Optional approach where hold services issue X.509 certificates based on ATProto signatures:

Use cases:

  • Enterprise environments requiring PKI compliance
  • Tools that only support X.509 (legacy systems)
  • Notation integration (P-256 certificates)

Trade-offs:

  • ❌ Introduces centralization (hold acts as CA)
  • ❌ Trust shifts from DIDs to hold operator
  • ❌ Requires hold service infrastructure

See: docs/HOLD_AS_CA.md for complete architecture and security considerations

Integration Strategy Decision Matrix#

Choose the right integration approach:

Use Case Recommended Approach Priority
Kubernetes admission control Ratify plugin or Gatekeeper provider HIGH
CI/CD verification atcr-verify CLI or GitHub Actions HIGH
Docker/containerd Containerd bindir plugin MEDIUM
Policy enforcement OPA Gatekeeper + external provider HIGH
Manual verification Shell scripts or atcr-verify CLI LOW
Enterprise PKI compliance Hold-as-CA (X.509 certificates) OPTIONAL
Legacy tool support Hold-as-CA or external bridge service OPTIONAL

See: docs/INTEGRATION_STRATEGY.md for complete integration planning guide including:

  • Architecture layers and data flow
  • Tool compatibility matrix (16+ tools)
  • Implementation roadmap (4 phases)
  • When to use each approach

Trust Policies#

Define which signatures you trust:

# trust-policy.yaml
version: 1.0

trustedDIDs:
  did:plc:alice123:
    name: "Alice (DevOps Lead)"
    validFrom: "2024-01-01T00:00:00Z"
    expiresAt: null

  did:plc:bob456:
    name: "Bob (Security Team)"
    validFrom: "2024-06-01T00:00:00Z"
    expiresAt: "2025-12-31T23:59:59Z"

policies:
  - name: production-images
    scope: "atcr.io/*/prod-*"
    require:
      signature: true
      trustedDIDs:
        - did:plc:alice123
        - did:plc:bob456
      minSignatures: 1
    action: enforce  # reject if policy fails

  - name: dev-images
    scope: "atcr.io/*/dev-*"
    require:
      signature: false
    action: audit  # log but don't reject

Use with:

  • atcr-verify CLI: atcr-verify IMAGE --policy trust-policy.yaml
  • Kubernetes webhooks: ConfigMap with policy
  • CI/CD pipelines: Fail build if policy not met

Security Considerations#

What ATProto Signatures Prove#

Identity: Manifest signed by specific DID (e.g., did:plc:alice123) ✅ Integrity: Manifest content hasn't been tampered with ✅ Timestamp: When the manifest was signed ✅ Authenticity: Signature created with private key for that DID

What They Don't Prove#

Vulnerability-free: Signature doesn't mean image is safe ❌ Authorization: DID ownership doesn't imply permission to deploy ❌ Key security: Private key could be compromised ❌ PDS trustworthiness: Malicious PDS could create fake records

Trust Dependencies#

When verifying signatures, you're trusting:

  1. DID resolution (PLC directory, did:web) - public key is correct for DID
  2. PDS integrity - PDS serves correct records and doesn't forge signatures
  3. Cryptographic primitives - ECDSA K-256 remains secure
  4. Your trust policy - DIDs you've chosen to trust are legitimate

Best Practices#

1. Use Trust Policies Don't blindly trust all signatures - define which DIDs you trust:

trustedDIDs:
  - did:plc:your-org-team
  - did:plc:your-ci-system

2. Monitor Signature Coverage Track which images have signatures:

atcr-verify --check-coverage namespace/production

3. Enforce in Production Use Kubernetes admission control to block unsigned images:

# Ratify + Gatekeeper or custom webhook
enforceSignatures: true
failurePolicy: Fail

4. Verify in CI/CD Never deploy unsigned images:

# GitHub Actions
- name: Verify signature
  run: atcr-verify $IMAGE || exit 1

5. Plan for Compromised Keys

  • Rotate DID keys periodically
  • Monitor DID documents for unexpected key changes
  • Have incident response plan for key compromise

Implementation Status#

✅ Available Now#

  • ATProto signatures: All manifests automatically signed by PDS
  • ORAS artifacts: Signature metadata stored as OCI artifacts
  • OCI Referrers API: Discovery via standard OCI endpoints
  • Shell scripts: Manual verification examples
  • Documentation: Complete integration guides

🔄 In Development#

  • atcr-verify CLI: Standalone verification tool
  • Ratify plugin: Kubernetes integration
  • Gatekeeper provider: OPA policy enforcement
  • GitHub Actions: CI/CD integration

📋 Planned#

  • Containerd plugin: Runtime-level verification
  • Hold-as-CA: X.509 certificate generation (optional)
  • Web UI: Signature viewer in AppView
  • Offline bundles: Air-gapped verification

Comparison with Other Signing Solutions#

Feature ATCR (ATProto) Cosign (Sigstore) Notation (Notary v2)
Signing Automatic (PDS) Manual or keyless Manual
Keys K-256 (secp256k1) P-256 or RSA P-256, P-384, P-521
Trust DID-based OIDC + Fulcio CA X.509 PKI
Storage ATProto PDS OCI registry OCI registry
Centralization Decentralized Centralized (Fulcio) Configurable
Transparency Log ATProto firehose Rekor Configurable
Verification Custom tools/plugins Cosign CLI Notation CLI
Kubernetes Plugins (Ratify) Policy Controller Policy Controller

ATCR advantages:

  • ✅ Decentralized trust (no CA required)
  • ✅ Automatic signing (no extra tools)
  • ✅ DID-based identity (portable, self-sovereign)
  • ✅ Transparent via ATProto firehose

ATCR trade-offs:

  • ⚠️ Requires custom verification tools/plugins
  • ⚠️ K-256 not supported by Notation (needs Hold-as-CA)
  • ⚠️ Smaller ecosystem than Cosign/Notation

Why Not Use Cosign Directly?#

Question: Why not just integrate with Cosign's keyless signing (OIDC + Fulcio)?

Answer: ATProto and Cosign use incompatible authentication models:

Requirement Cosign Keyless ATProto
Identity protocol OIDC ATProto OAuth + DPoP
Token format JWT from OIDC provider DPoP-bound access token
CA Fulcio (Sigstore CA) None (DID-based PKI)
Infrastructure Fulcio + Rekor + TUF PDS + DID resolver

To make Cosign work, we'd need to:

  1. Deploy Fulcio (certificate authority)
  2. Deploy Rekor (transparency log)
  3. Deploy TUF (metadata distribution)
  4. Build OIDC provider bridge for ATProto OAuth
  5. Maintain all this infrastructure

Instead: We leverage ATProto's existing signatures and build lightweight plugins/tools for verification. This is simpler, more decentralized, and aligns with ATCR's design philosophy.

For tools that need X.509 certificates: See Hold-as-CA for an optional centralized approach.

Getting Started#

Verify Your First Image#

# 1. Check if image has ATProto signature
oras discover atcr.io/alice/myapp:latest \
  --artifact-type application/vnd.atproto.signature.v1+json

# 2. Pull signature metadata
oras pull atcr.io/alice/myapp@sha256:sig789...

# 3. Verify with shell script
./examples/verification/atcr-verify.sh atcr.io/alice/myapp:latest

# 4. Use atcr-verify CLI (when available)
atcr-verify atcr.io/alice/myapp:latest --policy trust-policy.yaml

Deploy Kubernetes Verification#

# 1. Choose an approach
# Option A: Ratify plugin (recommended)
# Option B: Gatekeeper external provider
# Option C: Custom admission webhook

# 2. Follow integration guide
# See docs/SIGNATURE_INTEGRATION.md for step-by-step

# 3. Enable for namespace
kubectl label namespace production atcr-verify=enabled

# 4. Test with sample pod
kubectl run test --image=atcr.io/alice/myapp:latest -n production

Integrate with CI/CD#

# GitHub Actions
- name: Verify signature
  run: |
    curl -LO https://github.com/atcr-io/atcr/releases/latest/download/atcr-verify
    chmod +x atcr-verify
    ./atcr-verify ${{ env.IMAGE }} --policy .atcr/trust-policy.yaml

# GitLab CI
verify_image:
  script:
    - wget https://github.com/atcr-io/atcr/releases/latest/download/atcr-verify
    - chmod +x atcr-verify
    - ./atcr-verify $IMAGE --policy .atcr/trust-policy.yaml

Documentation#

Core Documentation#

Examples#

External References#

Support#

For questions or issues:

Summary#

Key Points:

  1. Automatic signing: Every ATCR image is automatically signed via ATProto's native signature system
  2. No additional tools: Signing happens transparently when you push images
  3. Decentralized trust: DID-based signatures, no central CA required
  4. Standard discovery: ORAS artifacts and OCI Referrers API for signature metadata
  5. Custom verification: Use plugins, CLI tools, or shell scripts (not Cosign directly)
  6. Multiple integrations: Kubernetes (Ratify, Gatekeeper), CI/CD (atcr-verify), containerd
  7. Optional X.509: Hold-as-CA for enterprise PKI compliance (centralized)

Next Steps:

  1. Read examples/verification/README.md for practical examples
  2. Choose integration approach from INTEGRATION_STRATEGY.md
  3. Implement plugin or deploy CLI tool from SIGNATURE_INTEGRATION.md
  4. Define trust policy for your organization
  5. Deploy to test environment first, then production