# How the Registry Component in Dewy Works: Artifact Discovery and Deployment Reporting

> Learn how Dewy's Registry component discovers build artifacts across storage backends using URL schemes like ghr:// s3:// and img:// It reports deployment status for efficient CI CD pipelines.

- Repository: [Tomohisa Oda/dewy](https://github.com/linyows/dewy)
- Tags: internals
- Published: 2026-03-06

---

**The Registry component in Dewy provides a unified interface for discovering the latest build artifact across multiple storage backends and reporting deployment status, using URL schemes like `ghr://`, `s3://`, and `img://` to select concrete implementations at runtime.**

The Registry component in Dewy serves as the abstraction layer that enables automatic artifact discovery and deployment tracking. As part of the `linyows/dewy` open-source deployment agent, this component decouples artifact storage specifics from the deployment logic, allowing operators to use GitHub Releases, Amazon S3, OCI registries, or custom gRPC services interchangeably.

## Core Interface and Design

At the heart of the Registry component in Dewy lies a minimal interface defined in [[`registry/registry.go`](https://github.com/linyows/dewy/blob/main/registry/registry.go)](https://github.com/linyows/dewy/blob/main/registry/registry.go#L22-L27). This contract ensures every backend implements two essential operations: retrieving the current artifact and reporting deployment results.

```go
type Registry interface {
    // Current returns the current artifact.
    Current(context.Context) (*CurrentResponse, error)
    // Report reports the result of deploying the artifact.
    Report(context.Context, *ReportRequest) error
}

```

The `CurrentResponse` struct encapsulates everything Dewy needs to download and deploy an artifact: the artifact ID, version tag, download URL, creation timestamp, and the deployment slot for blue/green strategies. By standardizing on this interface, Dewy can switch between GitHub Releases, S3 buckets, or container registries without modifying the core deployment loop.

## Factory Pattern: Selecting the Backend

Dewy uses a factory function to instantiate the correct Registry implementation based on the URL scheme. The `New` function in [[`registry/registry.go`](https://github.com/linyows/dewy/blob/main/registry/registry.go)](https://github.com/linyows/dewy/blob/main/registry/registry.go#L57-L77) parses the scheme prefix and delegates to the appropriate constructor.

```go
func New(ctx context.Context, url string, log *logging.Logger) (Registry, error) {
    splitted := strings.SplitN(url, "://", 2)

    switch splitted[0] {
    case ghrScheme:
        return NewGHR(ctx, url, log)
    case s3Scheme:
        return NewS3(ctx, url, log)
    case gsScheme:
        return NewGS(ctx, url, log)
    case grpcScheme:
        return NewGRPC(ctx, url)
    case imgScheme:
        return NewOCI(ctx, url, log)
    }
    return nil, fmt.Errorf("unsupported registry: %s", url)
}

```

This scheme-based routing enables flexible configuration. Operators can specify `ghr://github.com/owner/repo` for GitHub Releases, `s3://us-east-1/bucket/prefix` for Amazon S3, or `img://registry/repo` for OCI-compliant container registries. If the scheme is unrecognized, the factory returns an error immediately, preventing runtime failures during deployment.

## Version Selection and Slot Extraction

Once a Registry implementation identifies available versions, it must determine which artifact represents the "current" deployment target. Dewy supports both **SemVer** (Semantic Versioning) and **CalVer** (Calendar Versioning) schemes, allowing teams to use either `1.2.3` or `2024.01.02` style tags.

After selecting the latest version, the Registry extracts the **slot** identifier from the build metadata. This slot value (typically `blue` or `green`) enables zero-downtime blue/green deployments. The `extractSlot` helper in [[`registry/registry.go`](https://github.com/linyows/dewy/blob/main/registry/registry.go)](https://github.com/linyows/dewy/blob/main/registry/registry.go#L80-L95) handles both versioning schemes:

```go
func extractSlot(tag, calverFormat string) string {
    if calverFormat != "" {
        if f, err := NewCalVerFormat(calverFormat); err == nil {
            if cv := f.Parse(tag); cv != nil {
                return cv.BuildMetadata
            }
        }
    }
    if sv := ParseSemVer(tag); sv != nil {
        return sv.BuildMetadata
    }
    return ""
}

```

Each backend calls this function after identifying the latest tag, ensuring consistent slot extraction regardless of whether the artifact resides in GitHub Releases, S3, or a container registry.

## Concrete Backend Implementations

### GitHub Releases (`ghr://`)

The GitHub Releases backend, implemented in [[`registry/ghr.go`](https://github.com/linyows/dewy/blob/main/registry/ghr.go)](https://github.com/linyows/dewy/blob/main/registry/ghr.go), interacts with the GitHub API to list releases and download assets.

**Construction**: `NewGHR` parses the URL and extracts query parameters including `artifact` (the asset name), `pre-release` (whether to include pre-releases), and `calver` (the calendar version format). It initializes a GitHub client using `client.NewGitHub`.

**Finding the latest release**: The `latest` method lists all non-draft releases, builds a map of tag names to release objects, then selects the newest tag using either `FindLatestCalVer` or `FindLatestSemVer` based on configuration.

**Current implementation**: `Current` constructs an artifact URL in the format `ghr://owner/repo/tag/artifact`, extracts the deployment slot using `extractSlot`, and returns a `CurrentResponse` containing the download URL and metadata.

**Reporting**: `Report` uploads a small text file to the release assets indicating successful deployment, including timestamps and host information.

Key code locations:
- URL parsing: [`ghr.go:59-70`](https://github.com/linyows/dewy/blob/main/registry/ghr.go#L59-L70)
- Latest release selection: [`ghr.go:172-186`](https://github.com/linyows/dewy/blob/main/registry/ghr.go#L172-L186)
- Artifact URL construction: [`ghr.go:158-164`](https://github.com/linyows/dewy/blob/main/registry/ghr.go#L158-L164)

### Amazon S3 (`s3://`)

The S3 backend in [[`registry/s3.go`](https://github.com/linyows/dewy/blob/main/registry/s3.go)](https://github.com/linyows/dewy/blob/main/registry/s3.go) treats versioned directories as release artifacts, making it ideal for private binaries and air-gapped environments.

**Construction**: `NewS3` parses URLs in the format `s3://<region>/<bucket>/<prefix>` and configures the AWS SDK. It supports custom endpoints via the `AWS_ENDPOINT_URL` environment variable for local testing with MinIO or LocalStack.

**Latest version discovery**: `LatestVersion` lists directories (common prefixes) under the configured prefix rather than individual objects. It extracts version strings from directory names and applies `FindLatestCalVer` or `FindLatestSemVer` to identify the current release.

**Current implementation**: `Current` locates the specific artifact file within the version directory, either by exact name or by platform matching (OS/architecture detection). It retrieves the object's `LastModified` timestamp, constructs an `s3://` URL, and extracts the slot from the version directory name.

**Reporting**: `Report` creates an empty text object at `<prefix>/<tag>/<host>_<command>_<timestamp>.txt` to mark successful deployment.

Key code locations:
- URL parsing: [`s3.go:40-55`](https://github.com/linyows/dewy/blob/main/registry/s3.go#L40-L55)
- Latest version discovery: [`s3.go:69-104`](https://github.com/linyows/dewy/blob/main/registry/s3.go#L69-L104)
- Artifact URL builder: [`s3.go:68-80`](https://github.com/linyows/dewy/blob/main/registry/s3.go#L68-L80)

### OCI / Docker Registry (`img://`)

The OCI backend in [[`registry/oci.go`](https://github.com/linyows/dewy/blob/main/registry/oci.go)](https://github.com/linyows/dewy/blob/main/registry/oci.go) enables Dewy to deploy artifacts stored as container images, supporting Docker Hub, GitHub Container Registry, Amazon ECR, and other OCI-compliant registries.

**Construction**: `NewOCI` parses `img://registry/repo[:tag]` URLs and loads credentials from environment variables: `DOCKER_USERNAME` and `DOCKER_PASSWORD` for basic auth, or `GITHUB_TOKEN` for GitHub Container Registry.

**Listing tags**: `listTags` implements pagination-aware tag enumeration by repeatedly calling `fetchTagsPage`, following the `Link` header for Docker Registry API pagination (limited to 100 pages to prevent infinite loops).

**Authentication**: When receiving HTTP 401 responses, `getBearerToken` parses the `WWW-Authenticate` header to obtain a Bearer token. It validates the token realm URL against private IP blocks to prevent Server-Side Request Forgery (SSRF) attacks before retrying the request.

**Current implementation**: `Current` selects the newest tag using `findLatestTag`, then calls `getImageDigest` to fetch the manifest and extract the `Docker-Content-Digest` header as the artifact ID. The slot is extracted from the tag's build metadata using the same `extractSlot` helper as other backends.

**Reporting**: Currently a no-op for Phase 1, with future versions planned to support updating image labels or annotations to reflect deployment status.

Key code locations:
- Token handling: [`oci.go:57-90`](https://github.com/linyows/dewy/blob/main/registry/oci.go#L57-L90)
- Pagination: [`oci.go:47-66`](https://github.com/linyows/dewy/blob/main/registry/oci.go#L47-L66)
- Manifest request: [`oci.go:107-135`](https://github.com/linyows/dewy/blob/main/registry/oci.go#L107-L135)

## Error Handling with ArtifactNotFoundError

When an artifact cannot be located, all Registry implementations return a specific `ArtifactNotFoundError` rather than a generic error. This structured error type, defined in [[`registry/ghr.go`](https://github.com/linyows/dewy/blob/main/registry/ghr.go)](https://github.com/linyows/dewy/blob/main/registry/ghr.go#L19-L28), captures the artifact name, release time, and a descriptive message.

The error struct includes an `IsWithinGracePeriod` method that allows the deployment controller to distinguish between permanent failures and transient "not yet uploaded" situations. This is particularly useful when CI pipelines upload assets asynchronously; Dewy can retry the deployment rather than failing immediately when an artifact appears missing.

## Complete Usage Example

The following example demonstrates the complete workflow for initializing a Registry, querying the current artifact, and reporting deployment success:

```go
// Create a logger (omitted for brevity)
log := logging.NewLogger(...)

// Choose a registry URL (could be a flag, env var, etc.)
regURL := "ghr://github.com/owner/repo?artifact=mybinary&calver=2006.01.02"

// Build the appropriate Registry implementation
reg, err := registry.New(context.Background(), regURL, log)
if err != nil {
    // handle unsupported scheme, malformed URL, etc.
}

// Query the current artifact
cur, err := reg.Current(context.Background())
if err != nil {
    // maybe ArtifactNotFoundError – decide to retry later
}
fmt.Printf("Deploying %s (tag %s) from %s\n", cur.ArtifactURL, cur.Tag, cur.ID)

// After a successful deployment, report it
_ = reg.Report(context.Background(), &registry.ReportRequest{
    ID:      cur.ID,
    Tag:     cur.Tag,
    Command: "server", // or "assets"
    Err:     nil,      // nil means success
})

```

This pattern abstracts away the underlying storage mechanism, allowing the same deployment logic to work whether artifacts are stored in GitHub Releases, S3 buckets, or container registries.

## Summary

- The **Registry component in Dewy** defines a minimal interface with `Current` and `Report` methods, enabling artifact discovery and deployment tracking across diverse storage backends.
- A **factory function** in [`registry/registry.go`](https://github.com/linyows/dewy/blob/main/registry/registry.go) instantiates concrete implementations based on URL schemes (`ghr://`, `s3://`, `img://`, etc.), making the system extensible and configuration-driven.
- **Version selection** supports both SemVer and CalVer, with automatic extraction of build metadata slots (blue/green) from tag names to facilitate zero-downtime deployments.
- **Concrete backends** handle protocol-specific logic: GitHub Releases uses the GitHub API with asset uploads for reporting, S3 uses directory listing with platform matching, and OCI registries implement Docker Registry API authentication with pagination support.
- **Structured error handling** via `ArtifactNotFoundError` includes grace period detection, allowing the system to distinguish between transient upload delays and permanent missing artifacts.

## Frequently Asked Questions

### What URL schemes does the Dewy Registry support?

The Dewy Registry supports multiple URL schemes including `ghr://` for GitHub Releases, `s3://` for Amazon S3, `gs://` for Google Cloud Storage, `img://` for OCI/Docker registries, and `grpc://` for custom gRPC-based registries. Each scheme triggers a specific backend implementation via the factory function in [`registry/registry.go`](https://github.com/linyows/dewy/blob/main/registry/registry.go).

### How does the Registry component handle blue/green deployments?

The Registry extracts the deployment slot (typically `blue` or `green`) from the build metadata portion of version tags using the `extractSlot` function in [`registry/registry.go`](https://github.com/linyows/dewy/blob/main/registry/registry.go). This works with both SemVer tags like `1.0.0+blue` and CalVer tags like `2024.01.15+green`, allowing Dewy to route traffic to the appropriate deployment slot without downtime.

### What happens when an artifact cannot be found in the Registry?

When an artifact is missing, implementations return an `ArtifactNotFoundError` defined in [`registry/ghr.go`](https://github.com/linyows/dewy/blob/main/registry/ghr.go), which includes the artifact name, release time, and a grace period check via `IsWithinGracePeriod`. This allows Dewy to retry the deployment if the artifact is temporarily unavailable (e.g., still uploading from CI) rather than failing immediately.

### How does Dewy report successful deployments to the Registry?

After deploying an artifact, Dewy calls the `Report` method with a `ReportRequest` containing the artifact ID, tag, command type, and error status (nil for success). The GitHub Releases backend uploads a text file to the release assets, S3 creates an empty object at a specific key path, and the OCI backend currently implements this as a no-op with future support planned for label updates.