epoch

command module
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2026 License: MIT Imports: 8 Imported by: 0

README

epoch

A general-purpose OCI Distribution registry that hosts every flavor of cocoonstack artifact in one place:

  • Container imagesoras / crane / docker push and pull as usual.
  • OCI cloud images — disk-only artifacts (qcow2 / raw, including split parts) with artifactType: application/vnd.cocoonstack.os-image.v1+json. The windows builder publishes these to ghcr.io/cocoonstack/windows/win11:25h2 and the same shape works in epoch.
  • OCI VM snapshots — cocoon VM state captured by cocoon vm save and uploaded by epoch push as an OCI artifact with artifactType: application/vnd.cocoonstack.snapshot.v1+json.

Epoch is vendor-agnostic at the storage layer — the registry package only knows about blobs, manifests, and a global catalog. Cocoonstack-specific concepts (snapshot, cloudimg) live in the snapshot/ and cloudimg/ sub-packages on top.

Overview

  • Content-addressed storage — blobs deduplicated by SHA-256 digest
  • OCI 1.1 Distribution API/v2/ push/pull works with oras, crane, docker, containerd, buildah, and any OCI-compliant client
  • OCI artifact classification — top-level artifactType (cocoonstack cloud-image / snapshot) plus a config.mediaType fallback for plain container images
  • No filesystem couplingepoch push and epoch pull pipe through cocoon snapshot export / cocoon snapshot import / cocoon image import, so epoch never reads /var/lib/cocoon directly
  • MySQL metadata index — queryable catalog for the web UI and control API
  • SSO login — optional Google OAuth or generic OIDC for the web UI
  • Token management — create and revoke bearer tokens from the dashboard

Architecture

                          oras / crane / docker
                                  |
                                  ▼
                    ┌──────────────────────────┐
                    │ Epoch HTTP server        │
       ┌────────────│ /v2/  /api/  /dl/{name}  │────────────┐
       │            └──────────────────────────┘            │
       ▼                          ▼                          ▼
┌───────────────┐        ┌─────────────────┐        ┌─────────────────┐
│ snapshot pkg  │        │ registry pkg    │        │ cloudimg pkg    │
│ Push / Pull   │◄──────►│ blob/manifest   │◄──────►│ Stream / Pull   │
│ (cocoon pipe) │        │ + catalog.json  │        │ (cocoon pipe)   │
└───────────────┘        └─────────────────┘        └─────────────────┘
                                  │
                                  ▼
                          S3 / GCS bucket

Object layout in the bucket (under prefix epoch/):

catalog.json                            — global repository index
manifests/<repo>/<tag>.json             — manifest by tag
manifests/<repo>/_digests/<dgst>.json   — manifest by content digest
blobs/sha256/<dgst>                     — content-addressable blob

Artifact format

All three artifact kinds are standard OCI 1.1 image manifests. Epoch classifies them by looking at:

Field Value Kind
artifactType application/vnd.cocoonstack.os-image.v1+json cloud image
artifactType application/vnd.cocoonstack.snapshot.v1+json snapshot
config.mediaType application/vnd.oci.image.config.v1+json container image
config.mediaType application/vnd.docker.container.image.v1+json container image
mediaType (top) OCI image index / Docker manifest list container image (multi-arch)

A snapshot manifest looks like:

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "artifactType": "application/vnd.cocoonstack.snapshot.v1+json",
  "config": {
    "mediaType": "application/vnd.cocoonstack.snapshot.config.v1+json",
    "digest": "sha256:...",
    "size": 123
  },
  "layers": [
    { "mediaType": "application/vnd.cocoonstack.vm.config+json", "digest": "sha256:...", "annotations": {"org.opencontainers.image.title": "config.json"} },
    { "mediaType": "application/vnd.cocoonstack.vm.state+json", "digest": "sha256:...", "annotations": {"org.opencontainers.image.title": "state.json"} },
    { "mediaType": "application/vnd.cocoonstack.vm.memory",     "digest": "sha256:...", "annotations": {"org.opencontainers.image.title": "memory-ranges"} },
    { "mediaType": "application/vnd.cocoonstack.disk.qcow2",    "digest": "sha256:...", "annotations": {"org.opencontainers.image.title": "overlay.qcow2"} }
  ],
  "annotations": {
    "cocoonstack.snapshot.id": "sid-...",
    "cocoonstack.snapshot.baseimage": "ghcr.io/cocoonstack/cocoon/ubuntu:24.04",
    "org.opencontainers.image.created": "2026-04-09T..."
  }
}

A cloud-image manifest follows the same shape but uses artifactType: application/vnd.cocoonstack.os-image.v1+json and only carries disk layers (vnd.cocoonstack.disk.qcow2[.part] / vnd.cocoonstack.disk.raw[.part]).

Installation

Download

Grab a release tarball from GitHub Releases. Set VERSION to the release you want (the archive filename embeds it):

VERSION=0.1.6   # pick the latest release tag from the Releases page

# Linux (amd64)
curl -fSL https://github.com/cocoonstack/epoch/releases/download/v${VERSION}/epoch_${VERSION}_Linux_x86_64.tar.gz \
  | tar -xzf - epoch
sudo install -m 0755 epoch /usr/local/bin/epoch && rm epoch

# Linux (arm64)
curl -fSL https://github.com/cocoonstack/epoch/releases/download/v${VERSION}/epoch_${VERSION}_Linux_arm64.tar.gz \
  | tar -xzf - epoch
sudo install -m 0755 epoch /usr/local/bin/epoch && rm epoch

# macOS (Apple Silicon)
curl -fSL https://github.com/cocoonstack/epoch/releases/download/v${VERSION}/epoch_${VERSION}_Darwin_arm64.tar.gz \
  | tar -xzf - epoch
sudo install -m 0755 epoch /usr/local/bin/epoch && rm epoch

# macOS (Intel)
curl -fSL https://github.com/cocoonstack/epoch/releases/download/v${VERSION}/epoch_${VERSION}_Darwin_x86_64.tar.gz \
  | tar -xzf - epoch
sudo install -m 0755 epoch /usr/local/bin/epoch && rm epoch
Build from source
git clone https://github.com/cocoonstack/epoch.git
cd epoch
make build          # produces ./epoch

Configuration

Server
Variable Description
EPOCH_LOG_LEVEL debug / info / warn / error (default info)
EPOCH_PUBLIC_URL Absolute base URL clients reach the server at (e.g. https://epoch.example.com). Used to anchor the OCI WWW-Authenticate realm and /v2/token. Optional — when unset epoch reconstructs from the inbound Host + X-Forwarded-Proto, which works behind nginx but not behind proxies that strip those headers.
Object storage
Variable Description
EPOCH_S3_ENDPOINT S3 endpoint (with or without scheme)
EPOCH_S3_ACCESS_KEY Access key
EPOCH_S3_SECRET_KEY Secret key
EPOCH_S3_BUCKET Bucket name
EPOCH_S3_REGION Region (optional)
EPOCH_S3_SECURE true or false; inferred from scheme if omitted
EPOCH_S3_PREFIX Key prefix (default epoch/)
EPOCH_S3_ENV_FILE Env file path (default ~/.config/epoch/s3.env)
Authentication

Registry clients (/v2/):

  • Bearer token from EPOCH_REGISTRY_TOKEN or tokens created via the UI
  • Tokens are validated by SHA-256 hash against MySQL
  • When neither is set, /v2/ writes are open to anonymous clients

Web UI and control API:

  • Disabled by default (open access)
  • Set SSO_PROVIDER=google or SSO_PROVIDER=oidc to enable session-based login
  • See epoch-server.yaml for the full list of SSO variables
CLI client

epoch push / epoch pull / epoch get / epoch ls / epoch inspect / epoch tag / epoch rm talk to a remote epoch server over HTTP and read the following env vars:

Variable Description
EPOCH_SERVER Base URL of the epoch HTTP server (default http://127.0.0.1:8080)
EPOCH_REGISTRY_TOKEN Bearer token for write operations; same value as the server-side env
EPOCH_COCOON_BINARY Override the cocoon binary path used by epoch push / epoch pull (default looks up cocoon on $PATH)
Upload spool

In-progress chunked OCI uploads are spooled to disk so multi-GiB layers do not pin RAM. The directory MUST be backed by real disk — tmpfs (the default /tmp on most systemd hosts) defeats the spool and will OOM the host on big pushes.

Variable Description
EPOCH_UPLOAD_DIR Spool directory (default /var/cache/epoch/uploads; falls back to os.TempDir() with a loud warning if neither is creatable)

The bundled epoch-server.service already sets CacheDirectory=epoch and exports EPOCH_UPLOAD_DIR=/var/cache/epoch/uploads, so systemd deploys are configured out of the box.

Deployment files
Path Purpose
docker-compose.yaml Local MySQL and MinIO
epoch-server.yaml Kubernetes Deployment template
Dockerfile Container image build
epoch-server.service systemd unit file
epoch-nginx.conf nginx vhost — required tuning for streaming multi-GiB OCI pushes

Quick Start

Start local dependencies:

export MYSQL_ROOT_PASSWORD=changeme
export MYSQL_PASSWORD=changeme
export MINIO_ROOT_USER=minioadmin
export MINIO_ROOT_PASSWORD=changeme
docker compose up -d

Build and run:

make build

export EPOCH_S3_ENDPOINT=http://127.0.0.1:9000
export EPOCH_S3_ACCESS_KEY=minioadmin
export EPOCH_S3_SECRET_KEY=changeme
export EPOCH_S3_BUCKET=epoch
export EPOCH_S3_SECURE=false

./epoch serve --addr :8080 --dsn 'epoch:epoch@tcp(127.0.0.1:3306)/epoch?parseTime=true'

CLI

epoch serve                       # start HTTP server
epoch push NAME[:TAG]             # push a local cocoon snapshot
epoch pull NAME[:TAG]             # pull a snapshot or cloud image into cocoon
epoch get  NAME[:TAG]             # stream a cloud image's raw bytes to stdout
epoch ls   [NAME]                 # list repositories or tags
epoch inspect NAME[:TAG]          # show OCI manifest + classified kind
epoch tag SRC:OLD SRC:NEW         # re-tag a manifest in place
epoch rm  NAME:TAG                # remove a tag
epoch push <snapshot> (snapshots only)

Stream a cocoon snapshot into the registry as an OCI artifact:

epoch push myvm                                   # push myvm:latest
epoch push myvm -t v1                             # specific tag
epoch push myvm -t v1 \
  --base-image ghcr.io/cocoonstack/cocoon/ubuntu:24.04

The --base-image flag is optional; when set it lands as the cocoonstack.snapshot.baseimage annotation in the manifest. epoch never reads /var/lib/cocoon directly — it shells out to cocoon snapshot export -o - and uploads each tar entry as an OCI blob.

epoch pull <name>[:<tag>]

Fetch an artifact, classify it, and pipe it into cocoon:

Kind What happens
snapshot (vnd.cocoonstack.snapshot.v1+json) Reassemble tar → cocoon snapshot import --name <name>
cloud-image (vnd.cocoonstack.os-image.v1+json) Concatenate disk layers in title order → cocoon image import <name>
container-image Rejected — pull with oras / crane / docker and let cocoon's runtime use its built-in cocoon image pull ghcr.io/... path
epoch pull myvm:v1                       # snapshot, imports as "myvm"
epoch pull myvm:v1 --name myvm-restored  # snapshot with overridden local name
epoch pull windows/win11:25h2            # cloud image, imports as "win11"
epoch pull windows/win11:25h2 --name win11-test

The cocoon binary must be on $PATH; override with $EPOCH_COCOON_BINARY.

epoch get <name>[:<tag>] (cloud images only)

Stream the assembled disk bytes to stdout for piping. Snapshots cannot use epoch get because they are not single contiguous artifacts — use epoch pull instead.

epoch get windows/win11:25h2 | cocoon image import win11
ssh registry-host epoch get cocoon/ubuntu:24.04 | cocoon image import ubuntu

Progress goes to stderr so the data pipe stays clean.

Examples

Mirror a cocoonstack cloud image into epoch
# 1. Authenticate against epoch
echo $EPOCH_REGISTRY_TOKEN | crane auth login -u _token --password-stdin epoch.example

# 2. Mirror the upstream artifact (split-qcow2 windows image)
crane copy ghcr.io/cocoonstack/windows/win11:25h2 \
           epoch.example/windows/win11:25h2

# 3. Pull from epoch into cocoon on a node
epoch pull windows/win11:25h2

# Or, anonymously, via the public /dl/{name} short URL:
curl -fsSL https://epoch.example/dl/win11 | cocoon image import win11
Push a cocoon snapshot to epoch
# On a cocoon node — cocoon CLI must be on $PATH
cocoon vm save myvm                     # creates snapshot in /var/lib/cocoon
epoch push myvm -t v1 \
  --base-image ghcr.io/cocoonstack/cocoon/ubuntu:24.04
Pull and re-create a snapshot on another node
epoch pull myvm:v1 --name myvm-restored
cocoon vm clone --from myvm-restored --to myvm-clone
Mirror a plain container image

epoch pull rejects container images (cocoon doesn't consume them). Use OCI clients to mirror them through epoch:

crane copy ghcr.io/library/alpine:3.20 epoch.example/library/alpine:3.20
docker pull epoch.example/library/alpine:3.20

Development

make build          # build binary
make test           # race-detected tests with coverage
make lint           # golangci-lint for linux and darwin
make fmt            # gofumpt and goimports
make deps           # tidy modules
make all            # full pipeline
make help           # show all targets
Project Role
cocoon-common Shared metadata, Kubernetes, and logging helpers
cocoon-operator CocoonSet and Hibernation CRDs
cocoon-webhook Admission webhook for sticky scheduling
vk-cocoon Virtual kubelet provider managing VM lifecycle

License

MIT

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
Package cloudimg streams OCI-packaged cloud images out of an Epoch registry and into `cocoon image import`.
Package cloudimg streams OCI-packaged cloud images out of an Epoch registry and into `cocoon image import`.
Package manifest defines the OCI manifest types and cocoonstack artifact-classification helpers used by epoch.
Package manifest defines the OCI manifest types and cocoonstack artifact-classification helpers used by epoch.
Package registry implements the storage layer of the Epoch OCI registry.
Package registry implements the storage layer of the Epoch OCI registry.
Package registryclient is a small HTTP client for talking to an Epoch (or any OCI Distribution-compatible) registry.
Package registryclient is a small HTTP client for talking to an Epoch (or any OCI Distribution-compatible) registry.
Package server implements the Epoch HTTP server.
Package server implements the Epoch HTTP server.
Package snapshot pushes and pulls cocoon VM snapshots as OCI artifacts.
Package snapshot pushes and pulls cocoon VM snapshots as OCI artifacts.
Package store provides MySQL-backed metadata storage for the Epoch control plane.
Package store provides MySQL-backed metadata storage for the Epoch control plane.
Package ui embeds the frontend static files.
Package ui embeds the frontend static files.
Package utils provides shared helper functions used across epoch packages.
Package utils provides shared helper functions used across epoch packages.
Package version holds build-time version metadata, injected via ldflags.
Package version holds build-time version metadata, injected via ldflags.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL