csha
    • CSha
    • Building and installation
    • Commands and examples
    • Architecture
    • Changelog
    • AI usage
    • License
    • Quick start
    • Blob Streams
    • Docker images as PNGs
    • Formats and keys
    • C++ API
    • Troubleshooting
    • Encryption algorithms
    • Testing

    Docker images as PNGs

    Start with a Docker reference such as redis:7.4-alpine, my-app:latest, or
    registry.example.com/team/app:v2. CSha resolves the reference to its exact
    local image ID, saves the image's layers and configuration, and encrypts that
    archive into a real RGB PNG. The pixels look like colored noise. This preserves
    the Docker image, including its entrypoint, command, and image filesystem.
    Docker must be installed and its daemon reachable for pack, load, and run.
    keygen, inspect, and restore work without Docker. Registry authentication
    uses your existing Docker login. No registry credentials are stored in the PNG.

    Create a PNG from a tag#

    csha docker-image keygen --output app.docker-key
    csha docker-image pack --image my-app:latest \
      --key-file app.docker-key --output app.png
    
    # Explicitly pull a registry tag before packing it:
    csha docker-image pack --image redis:7.4-alpine --pull \
      --key-file app.docker-key --output redis.png
    Without --pull, packing uses the local image and fails if it is unavailable.
    The image is saved by its resolved ID, so a concurrent change to its tag cannot
    silently select a different image. Output files are created with mode 0600;
    existing output paths are never overwritten. Keep the key separate from the
    PNG. The key is 32 random bytes, not a textual password; losing it means losing
    access to the encrypted archive.

    Inspect, restore, and load#

    # Verifies the entire encrypted archive before showing its metadata.
    csha docker-image inspect --input app.png --key-file app.docker-key
    
    # Produce a normal docker-save tar file, without needing Docker installed.
    csha docker-image restore --input app.png --key-file app.docker-key \
      --output app.tar
    # Equivalent manual use of that archive:
    docker image load --input app.tar
    
    # Restore and load directly, optionally assign a tag:
    csha docker-image load --input app.png --key-file app.docker-key \
      --tag restored-app:v1
    Inspection reports the source reference, canonical image ID, archive byte count,
    and archive SHA-256. Loading without --tag restores by ID; it does not reassign
    the source tag. An explicit --tag may replace an existing local tag. CSha
    checks that the expected image ID is present after Docker loads the archive.

    Run the restored image#

    # Use the image's original entrypoint and command.
    csha docker-image run --input app.png --key-file app.docker-key \
      --name restored-app --publish 8080:80
    
    # Override the command (Docker entrypoint semantics still apply).
    csha docker-image run --input redis.png --key-file app.docker-key \
      --network none -- /bin/sh -c 'printf "restored image works\n"'
    
    # Detached service; stop it later using Docker.
    csha docker-image run --input app.png --key-file app.docker-key \
      --detach --name restored-service --env MODE=production --publish 8080:80
    docker stop restored-service
    run decrypts, verifies, loads, then invokes docker run --rm --pull=never
    using the authenticated canonical ID. It propagates Docker/container exit
    codes. Options are --name, repeatable --publish and --env, --network,
    and --detach. Arguments following -- are passed directly to Docker as the
    container command. CSha does not interpolate shell commands. For other Docker
    options, use load, then docker run with the reported ID or your chosen tag.

    Format and limits#

    This is a lossless data container: resizing, recompressing as JPEG, or changing
    pixels prevents recovery. It uses its own versioned format, separate from CSha's
    biome maps and image-password formats. It does not contain a running container's
    memory, writable container layer, or volumes; it contains a saved Docker image.
    A Docker daemon capable of running that image's platform is still required.
    By default the archive and its metadata use AES-256-GCM and a fresh 96-bit
    nonce per PNG. pack --cipher NAME selects one of the ten additional algorithms
    listed in encryption algorithms; inspection reports the cipher,
    and restore/load/run detect it automatically. The envelope header is authenticated too. Authentication, archive
    SHA-256, pixel padding, and PNG checks complete before an output archive is
    published or Docker is invoked. During decryption, provisional plaintext is
    written only to a private temporary file and discarded on failure.
    The pixel stream is RGB8, non-interlaced, with an envelope header, encrypted
    metadata and tar bytes, an authentication tag, and final zero padding.
    Metadata includes the original reference and canonical Docker image ID. The
    implementation streams 64 KiB archive blocks and one PNG row, rather than
    holding the image or archive in memory. The archive limit is 63 GiB, below the
    single-message AES-GCM size limit. Some image viewers cannot display PNGs of
    this size. PNG output is slightly larger than the saved tar, since encrypted
    bytes cannot be usefully compressed.
    Packing needs temporary disk space for the saved archive plus the output PNG.
    Loading/running needs space for a decrypted archive in the system temporary
    directory, in addition to Docker's image storage. Normal completion or errors
    remove temporary files; a forced kill or power loss can leave private temporary
    files behind. Restored tar files are plaintext. Authentication establishes
    possession of the encryption key; it does not establish the publisher or safety
    of the software inside the image.

    Verification#

    The default CTest suite runs the real PNG codec and CLI against a deterministic
    Docker subprocess fixture, including tampering, wrong keys, output protection,
    exit codes, and argument forwarding. An optional real-daemon check creates and
    removes its own test image, restores it from PNG after removal, then runs a
    locally available image (which must include /bin/sh) with networking disabled:
    python3 tests/test_docker_live.py ./build/csha redis:7.4-alpine
    This check does not pull an image.

    Command and dependency reference#

    CommandRequires DockerWrites or changes
    keygen --output KEYNoNew 32-byte key file
    pack --image REF --key-file KEY --output PNGYesEncrypted PNG; --pull may download an image
    inspect --input PNG --key-file KEYNoVerified metadata on stdout
    restore --input PNG --key-file KEY --output TARNoDecrypted Docker-save archive
    load --input PNG --key-file KEYYesDocker image store; optional --tag assigns/replaces a tag
    run --input PNG --key-file KEYYesImage store and a container; --detach leaves it running
    --publish and --env can be repeated. Options after -- are container command
    arguments, so put CSha's --name, --network, and other options before it.
    The supported wrapper options are intentionally limited; use load followed
    by Docker directly when you need another runtime option.

    A reproducible recovery check#

    Use a tag already present locally and a new output directory. Capture the
    canonical image ID before packaging, then compare it with inspect after
    recovery. For a plaintext archive comparison, use a hash of the same archive
    that was packaged; two separate docker save invocations need not be treated
    as byte-identical merely because they represent the same image.
    inspect checks the archive SHA-256 stored inside the authenticated envelope.
    load additionally confirms the canonical image ID is present. run selects
    that authenticated ID rather than trusting a tag that could have moved since
    packaging. These checks establish archive consistency; they do not scan the
    image contents for vulnerabilities.

    Files to keep#

    Keep the original lossless PNG and the matching binary key in separate places.
    A running container, a detached container ID, or a renamed tag cannot substitute
    for the encryption key. The source tag is metadata; its registry may change or
    stop serving the image without changing the saved PNG.
    For restore, authentication, and disk-space errors, see
    Troubleshooting. For use without the CLI, see
    C++ API.
    Modified at 2026-10-17 15:07:09
    Previous
    Blob Streams
    Next
    Formats and keys
    Built with