{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Config",
  "description": "Top-level configuration defining registries, mappings, and sync behavior.",
  "type": "object",
  "required": [
    "mappings"
  ],
  "properties": {
    "defaults": {
      "description": "Default source, targets, tags, and platforms inherited by all mappings.",
      "default": null,
      "anyOf": [
        {
          "$ref": "#/definitions/DefaultsConfig"
        },
        {
          "type": "null"
        }
      ]
    },
    "global": {
      "description": "Global engine settings applied across all syncs.",
      "default": null,
      "anyOf": [
        {
          "$ref": "#/definitions/GlobalConfig"
        },
        {
          "type": "null"
        }
      ]
    },
    "mappings": {
      "description": "Image mapping rules defining what to sync and where.",
      "type": "array",
      "items": {
        "$ref": "#/definitions/MappingConfig"
      }
    },
    "registries": {
      "description": "Named registry definitions keyed by alias.",
      "default": {},
      "type": "object",
      "additionalProperties": {
        "$ref": "#/definitions/RegistryConfig"
      }
    },
    "target_groups": {
      "description": "Named groups of registry aliases for multi-target fanout.",
      "default": {},
      "type": "object",
      "additionalProperties": {
        "type": "array",
        "items": {
          "type": "string"
        }
      }
    }
  },
  "definitions": {
    "ArtifactsConfig": {
      "description": "Configuration for OCI artifact (signatures, SBOMs, attestations) sync.\n\nControls whether referrers are discovered and transferred alongside their parent image manifests.",
      "type": "object",
      "properties": {
        "enabled": {
          "description": "Whether artifact sync is enabled (default: true).",
          "default": true,
          "type": "boolean"
        },
        "exclude": {
          "description": "Exclude artifacts whose artifact type matches one of these MIME types.",
          "default": [],
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "include": {
          "description": "Only sync artifacts whose artifact type matches one of these MIME types.",
          "default": [],
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "require_artifacts": {
          "description": "When true, every synced image must have at least one referrer. Images without referrers cause a sync failure instead of silently producing unsigned images at the target.",
          "default": false,
          "type": "boolean"
        }
      }
    },
    "AuthType": {
      "description": "Authentication method for a registry.",
      "oneOf": [
        {
          "description": "AWS ECR token exchange.",
          "type": "string",
          "enum": [
            "ecr"
          ]
        },
        {
          "description": "Google Artifact Registry native auth (ADC).",
          "type": "string",
          "enum": [
            "gar"
          ]
        },
        {
          "description": "Google Container Registry native auth (ADC).",
          "type": "string",
          "enum": [
            "gcr"
          ]
        },
        {
          "description": "Azure Container Registry native auth (AAD).",
          "type": "string",
          "enum": [
            "acr"
          ]
        },
        {
          "description": "GitHub Container Registry (`GITHUB_TOKEN`).",
          "type": "string",
          "enum": [
            "ghcr"
          ]
        },
        {
          "description": "Anonymous (token exchange only).",
          "type": "string",
          "enum": [
            "anonymous"
          ]
        },
        {
          "description": "HTTP basic auth.",
          "type": "string",
          "enum": [
            "basic"
          ]
        },
        {
          "description": "Pre-obtained bearer token (PAT, CI token).",
          "type": "string",
          "enum": [
            "static_token"
          ]
        },
        {
          "description": "Docker config.json credential store.",
          "type": "string",
          "enum": [
            "docker_config"
          ]
        }
      ]
    },
    "BasicCredentials": {
      "description": "Credentials for HTTP Basic authentication.",
      "type": "object",
      "required": [
        "password",
        "username"
      ],
      "properties": {
        "password": {
          "description": "Password or access token.",
          "type": "string"
        },
        "username": {
          "description": "Username for authentication.",
          "type": "string"
        }
      }
    },
    "DefaultsConfig": {
      "description": "Default values inherited by all mappings unless individually overridden.",
      "type": "object",
      "properties": {
        "artifacts": {
          "description": "Artifact sync configuration applied to all mappings unless overridden.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/ArtifactsConfig"
            },
            {
              "type": "null"
            }
          ]
        },
        "platforms": {
          "description": "Platform filter applied to all mappings unless overridden.\n\nEach entry must be `os/arch` or `os/arch/variant` (e.g. `linux/amd64`, `linux/arm/v7`).",
          "default": null,
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "string"
          }
        },
        "source": {
          "description": "Default source registry alias for all mappings.",
          "default": null,
          "type": [
            "string",
            "null"
          ]
        },
        "tags": {
          "description": "Default tag filter rules inherited by mappings without their own.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/TagsConfig"
            },
            {
              "type": "null"
            }
          ]
        },
        "targets": {
          "description": "Default target registry alias or group for all mappings.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/TargetsValue"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "GlobOrList": {
      "description": "A glob pattern: either a single string or a list of patterns.",
      "anyOf": [
        {
          "description": "A single glob pattern string.",
          "type": "string"
        },
        {
          "description": "Multiple glob pattern strings.",
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "GlobalConfig": {
      "description": "Global engine settings that apply across all sync operations.",
      "type": "object",
      "properties": {
        "cache_dir": {
          "description": "Cache directory for persistent cache and blob staging.\n\nDefaults to a directory next to the config file when not specified.",
          "type": [
            "string",
            "null"
          ]
        },
        "cache_ttl": {
          "description": "Warm cache TTL as a human-readable duration (e.g. \"12h\", \"30m\").\n\n`\"0\"` disables TTL-based expiry (cache never expires by age; lazy invalidation only). Defaults to `\"12h\"` when not specified.",
          "type": [
            "string",
            "null"
          ]
        },
        "max_concurrent_transfers": {
          "description": "Maximum concurrent image syncs (default: 50).",
          "default": 50,
          "type": "integer",
          "format": "uint",
          "minimum": 0.0
        },
        "staging_size_limit": {
          "description": "Disk staging size limit as a human-readable size (e.g. \"2GB\", \"500MB\").\n\nUses SI decimal prefixes: 1 GB = 1,000,000,000 bytes. `0` disables disk staging. When absent, no eviction is performed.",
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "MappingConfig": {
      "description": "A single image mapping rule: source repo, target registries, and tag filters.",
      "type": "object",
      "required": [
        "from"
      ],
      "properties": {
        "artifacts": {
          "description": "Artifact sync configuration for this mapping, overriding `defaults.artifacts`.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/ArtifactsConfig"
            },
            {
              "type": "null"
            }
          ]
        },
        "from": {
          "description": "Source repository path (e.g. `library/nginx`).",
          "type": "string"
        },
        "platforms": {
          "description": "Platform filter for this mapping, overriding any value in `defaults`.\n\nEach entry must be `os/arch` or `os/arch/variant` (e.g. `linux/amd64`, `linux/arm/v7`).",
          "default": null,
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": "string"
          }
        },
        "source": {
          "description": "Source registry alias, overriding `defaults.source`.",
          "default": null,
          "type": [
            "string",
            "null"
          ]
        },
        "tags": {
          "description": "Tag filter rules for this mapping, overriding `defaults.tags`.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/TagsConfig"
            },
            {
              "type": "null"
            }
          ]
        },
        "targets": {
          "description": "Target registry alias or group, overriding `defaults.targets`.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/TargetsValue"
            },
            {
              "type": "null"
            }
          ]
        },
        "to": {
          "description": "Destination repository path; defaults to `from` when absent.",
          "default": null,
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "RegistryConfig": {
      "description": "Per-registry settings: URL, auth method, concurrency, and credentials.",
      "type": "object",
      "required": [
        "url"
      ],
      "properties": {
        "auth_type": {
          "description": "Authentication method to use for this registry.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/AuthType"
            },
            {
              "type": "null"
            }
          ]
        },
        "aws_profile": {
          "description": "Named AWS profile for ECR credential resolution.\n\nWhen set, ECR auth for this registry uses `aws_config::ConfigLoader::profile_name(p)` instead of the ambient credential chain. Only valid with explicit `auth_type: ecr`.\n\nUse this when the registry requires credentials distinct from the ambient identity (for example, a third-party ECR accessed with static IAM-user keys while the ambient chain serves the rest of the workload).",
          "default": null,
          "type": [
            "string",
            "null"
          ]
        },
        "credentials": {
          "description": "Credentials for Basic auth (`auth_type: basic`).",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/BasicCredentials"
            },
            {
              "type": "null"
            }
          ]
        },
        "head_first": {
          "description": "HEAD-check targets before pulling full source manifests on cache miss.\n\nWhen enabled, the engine issues a manifest HEAD against all targets before performing a full source manifest GET. If every target already holds the same digest as the source HEAD, the expensive GET is skipped. This conserves rate-limit tokens on source registries with aggressive quotas (e.g., Docker Hub).",
          "default": false,
          "type": "boolean"
        },
        "max_concurrent": {
          "description": "Per-registry aggregate concurrency cap (default: 50).\n\nLimits the total number of simultaneous in-flight HTTP requests to this registry across all action types. This is independent of the global `max_concurrent_transfers` (which caps image-level parallelism).",
          "type": [
            "integer",
            "null"
          ],
          "format": "uint",
          "minimum": 0.0
        },
        "token": {
          "description": "Bearer token for static token auth (`auth_type: static_token`).",
          "default": null,
          "type": [
            "string",
            "null"
          ]
        },
        "url": {
          "description": "Registry base URL (e.g. `registry-1.docker.io`, `123456789012.dkr.ecr.us-east-1.amazonaws.com`).",
          "type": "string"
        }
      }
    },
    "SemverPrerelease": {
      "description": "How to handle semver pre-release tags.",
      "oneOf": [
        {
          "description": "Include pre-release tags in results.",
          "type": "string",
          "enum": [
            "include"
          ]
        },
        {
          "description": "Exclude pre-release tags from results.",
          "type": "string",
          "enum": [
            "exclude"
          ]
        },
        {
          "description": "Return only pre-release tags.",
          "type": "string",
          "enum": [
            "only"
          ]
        }
      ]
    },
    "SortOrder": {
      "description": "Sort order for the final tag list.",
      "oneOf": [
        {
          "description": "Sort by semantic version (highest first).",
          "type": "string",
          "enum": [
            "semver"
          ]
        },
        {
          "description": "Sort alphabetically (highest first).",
          "type": "string",
          "enum": [
            "alpha"
          ]
        }
      ]
    },
    "TagsConfig": {
      "description": "Tag filter and selection rules for a mapping.",
      "type": "object",
      "properties": {
        "exclude": {
          "description": "Exclude tags matching one or more glob patterns.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/GlobOrList"
            },
            {
              "type": "null"
            }
          ]
        },
        "glob": {
          "description": "Include tags matching one or more glob patterns.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/GlobOrList"
            },
            {
              "type": "null"
            }
          ]
        },
        "immutable_tags": {
          "description": "Glob pattern for immutable tags (e.g. `\"v?[0-9]*.[0-9]*.[0-9]*\"`).\n\nWhen a tag matches this pattern AND already exists in the target's tag list, the tag is skipped with zero API calls. Semver tags are conventionally immutable; this avoids redundant HEAD checks.",
          "default": null,
          "type": [
            "string",
            "null"
          ]
        },
        "latest": {
          "description": "Keep only the N most recent tags after sorting.",
          "default": null,
          "type": [
            "integer",
            "null"
          ],
          "format": "uint",
          "minimum": 0.0
        },
        "min_tags": {
          "description": "Minimum number of tags to retain regardless of filters.",
          "default": null,
          "type": [
            "integer",
            "null"
          ],
          "format": "uint",
          "minimum": 0.0
        },
        "semver": {
          "description": "Include tags matching a semver range (e.g. `>=1.0, <2.0`).",
          "default": null,
          "type": [
            "string",
            "null"
          ]
        },
        "semver_prerelease": {
          "description": "Whether to include or exclude semver pre-release tags.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/SemverPrerelease"
            },
            {
              "type": "null"
            }
          ]
        },
        "sort": {
          "description": "Sort order applied before `latest` truncation.",
          "default": null,
          "anyOf": [
            {
              "$ref": "#/definitions/SortOrder"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "TargetsValue": {
      "description": "Target specification: either a named group or an inline list of registry aliases.",
      "anyOf": [
        {
          "description": "A single target group name defined in `target_groups`.",
          "type": "string"
        },
        {
          "description": "An inline list of registry aliases.",
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    }
  }
}
