{"openapi":"3.1.0","info":{"title":"Molto Registry","version":"0.1.0","description":"Artifact registry for the Molto ecosystem. Serves toolchains to pickup and packages to molto over one protocol."},"components":{"schemas":{"LoginResponse":{"type":"object","properties":{"token":{"type":"string","description":"Bearer token; shown once and never stored"},"email":{"type":"string"},"name":{"type":"string"},"created_at":{"type":"string"}},"required":["token","email","name","created_at"]},"Error":{"type":"object","properties":{"error":{"type":"string","description":"Stable machine-readable code, safe to branch on"},"message":{"type":"string","description":"Human-readable detail; may change"}},"required":["error","message"]},"LoginRequest":{"type":"object","properties":{"email":{"type":"string","minLength":1,"maxLength":254},"password":{"type":"string","minLength":1,"maxLength":256},"name":{"type":"string","maxLength":64,"default":"molto login","description":"What the token is for, so its owner can recognise it"}},"required":["email","password"]},"Health":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"service":{"type":"string"},"environment":{"type":"string"}},"required":["status","service","environment"]},"SearchResults":{"type":"object","properties":{"query":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"},"description":"Prefix matches first, then alphabetically"}},"required":["query","results"]},"SearchResult":{"type":"object","properties":{"kind":{"type":"string","enum":["toolchain","package","tool"]},"name":{"type":"string"},"latest_version":{"type":"string"},"targets":{"type":"integer","minimum":0,"description":"Targets built for that version"},"published_at":{"type":"string"},"published_by":{"type":["string","null"],"description":"Who published the latest version, if it is known"}},"required":["kind","name","latest_version","targets","published_at","published_by"]},"CatalogueList":{"type":"object","properties":{"kind":{"type":"string","enum":["toolchain","package","tool"]},"entries":{"type":"array","items":{"$ref":"#/components/schemas/CatalogueEntry"},"description":"Alphabetically by name"}},"required":["kind","entries"]},"CatalogueEntry":{"type":"object","properties":{"name":{"type":"string"},"latest_version":{"type":"string"},"versions":{"type":"integer","minimum":0,"description":"How many versions exist under this name"},"targets":{"type":"array","items":{"type":"string"},"description":"Targets the latest version was built for"},"published_at":{"type":"string"},"published_by":{"type":["string","null"],"description":"Who published the latest version, if it is known"}},"required":["name","latest_version","versions","targets","published_at","published_by"]},"ReleaseList":{"type":"object","properties":{"kind":{"type":"string","enum":["toolchain","package","tool"]},"name":{"type":"string"},"releases":{"type":"array","items":{"$ref":"#/components/schemas/Release"},"description":"Every version of this name, most recently published first"}},"required":["kind","name","releases"]},"Release":{"type":"object","properties":{"kind":{"type":"string","enum":["toolchain","package","tool"]},"name":{"type":"string"},"version":{"type":"string"},"targets":{"type":"array","items":{"$ref":"#/components/schemas/Artifact"}}},"required":["kind","name","version","targets"]},"Artifact":{"type":"object","properties":{"kind":{"type":"string","enum":["toolchain","package","tool"]},"form":{"type":"string","enum":["binary","source"]},"name":{"type":"string"},"version":{"type":"string"},"target":{"type":"string"},"format":{"type":["string","null"],"description":"How the blob is packed, e.g. tar.zst. Never inferred from the URL. Null for a source recipe"},"checksum":{"type":["string","null"],"description":"Lowercase hex SHA-256 of the artifact blob. Null for a source recipe"},"size_bytes":{"type":["integer","null"],"minimum":0},"yanked":{"type":"boolean","description":"Still resolvable, but must not be used for new builds"},"published_at":{"type":"string"},"published_by":{"type":["string","null"],"description":"The account that published it. Null for the artifacts that predate accounts"},"metadata":{"type":"object","additionalProperties":{}},"download_url":{"type":["string","null"],"format":"uri","description":"Null for a source recipe: there are no bytes to download"}},"required":["kind","form","name","version","target","format","checksum","size_bytes","yanked","published_at","published_by","metadata","download_url"]},"BlobUpload":{"type":"object","properties":{"storage_key":{"type":"string"},"size_bytes":{"type":"integer","minimum":0},"checksum":{"type":["string","null"]}},"required":["storage_key","size_bytes","checksum"]}},"parameters":{}},"paths":{"/v1/auth/token":{"post":{"tags":["auth"],"operationId":"create_token","summary":"Exchange an email and password for a publishing token","requestBody":{"description":"The account's credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"201":{"description":"The token, which is not recoverable afterwards","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"That email and password do not match an account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/health":{"get":{"tags":["meta"],"operationId":"health","summary":"Liveness probe","responses":{"200":{"description":"The Worker is serving requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/v1/search":{"get":{"tags":["search"],"operationId":"search","summary":"Find artifact names matching a substring","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"example":"clang"},"required":true,"name":"q","in":"query"},{"schema":{"type":"string","enum":["toolchain","package","tool"]},"required":false,"name":"kind","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"example":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Matching names, best match first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResults"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains":{"get":{"tags":["toolchains"],"operationId":"list_toolchains","summary":"The catalogue index of published toolchains","description":"One entry per name, each with its latest version and the targets that version was built for — enough to tell whether this registry serves your platform without asking again per name.","responses":{"200":{"description":"The catalogue index","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueList"}}}}}},"post":{"tags":["toolchains"],"operationId":"publish_toolchain","summary":"Record a toolchain whose blob is already uploaded","description":"The body is the `recipe.toml`, verbatim: it carries the coordinate and is stored whole as the artifact's metadata. The checksum and the size are measured from the stored blob, never declared, so a recipe cannot describe an archive other than the one that was uploaded. Written last on purpose: a catalogue entry without its blob is the one inconsistency the API cannot serve around. A recipe declaring `form = \"source\"` is the exception and the whole request: it has no blob anywhere, so nothing is uploaded first and nothing is measured.","requestBody":{"description":"The recipe.toml describing exactly one coordinate","content":{"application/toml":{"schema":{"type":"string"}}}},"responses":{"201":{"description":"The artifact is now in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artifact"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No blob has been uploaded for that coordinate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That coordinate is already published, and they are immutable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains/{name}":{"get":{"tags":["toolchains"],"operationId":"list_toolchain_releases","summary":"Every release published under one toolchain name","description":"Each release carries its targets in full: format, size, checksum and download URL. Resolving a name to something downloadable is this one request.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"Releases, most recently published first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseList"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains/{name}/{version}":{"get":{"tags":["toolchains"],"operationId":"get_toolchain_release","summary":"List every target built for one version","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"}],"responses":{"200":{"description":"The release and its targets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains/{name}/{version}/{target}":{"get":{"tags":["toolchains"],"operationId":"get_toolchain_artifact","summary":"Describe one exact artifact","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"200":{"description":"The artifact and how to download it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artifact"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains/{name}/{version}/{target}/download":{"get":{"tags":["toolchains"],"operationId":"download_toolchain","summary":"Stream the artifact bytes","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"200":{"description":"The artifact blob. `x-molto-checksum` carries its SHA-256 and `x-molto-yanked` whether it was withdrawn.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The blob is catalogued but unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains/{name}/{version}/{target}/blob":{"put":{"tags":["toolchains"],"operationId":"upload_toolchain_blob","summary":"Upload the archive for one coordinate","description":"Streams the tar.zst into object storage, which verifies it hashes to `x-molto-checksum` and rejects it if it does not. The artifact stays invisible until the metadata is posted.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"},{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Lowercase hex SHA-256 the uploaded blob must match"},"required":true,"description":"Lowercase hex SHA-256 the uploaded blob must match","name":"x-molto-checksum","in":"header"}],"requestBody":{"description":"The archive bytes","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"201":{"description":"The blob was stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlobUpload"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That coordinate is already published, and they are immutable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains/{name}/{version}/{target}/yank":{"post":{"tags":["toolchains"],"operationId":"yank_toolchain","summary":"Withdraw a version from new resolutions","description":"Marks the coordinate so that resolutions starting now do not choose it. The bytes stay, the download keeps working and carries `x-molto-yanked`, and a lock file that already names this version keeps building. Coordinates are immutable, so this is the only remedy for a publish that should not have happened. Idempotent.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"204":{"description":"The artifact is now yanked"},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such coordinate in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/toolchains/{name}/{version}/{target}/unyank":{"post":{"tags":["toolchains"],"operationId":"unyank_toolchain","summary":"Put a withdrawn version back","description":"Clears the withdrawal, making the version eligible again. Idempotent.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"204":{"description":"The artifact is now unyanked"},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such coordinate in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages":{"get":{"tags":["packages"],"operationId":"list_packages","summary":"The catalogue index of published packages","description":"One entry per name, each with its latest version and the targets that version was built for — enough to tell whether this registry serves your platform without asking again per name.","responses":{"200":{"description":"The catalogue index","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueList"}}}}}},"post":{"tags":["packages"],"operationId":"publish_package","summary":"Record a package whose blob is already uploaded","description":"The body is the `recipe.toml`, verbatim: it carries the coordinate and is stored whole as the artifact's metadata. The checksum and the size are measured from the stored blob, never declared, so a recipe cannot describe an archive other than the one that was uploaded. Written last on purpose: a catalogue entry without its blob is the one inconsistency the API cannot serve around. A recipe declaring `form = \"source\"` is the exception and the whole request: it has no blob anywhere, so nothing is uploaded first and nothing is measured.","requestBody":{"description":"The recipe.toml describing exactly one coordinate","content":{"application/toml":{"schema":{"type":"string"}}}},"responses":{"201":{"description":"The artifact is now in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artifact"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No blob has been uploaded for that coordinate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That coordinate is already published, and they are immutable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages/{name}":{"get":{"tags":["packages"],"operationId":"list_package_releases","summary":"Every release published under one package name","description":"Each release carries its targets in full: format, size, checksum and download URL. Resolving a name to something downloadable is this one request.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"Releases, most recently published first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseList"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages/{name}/{version}":{"get":{"tags":["packages"],"operationId":"get_package_release","summary":"List every target built for one version","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"}],"responses":{"200":{"description":"The release and its targets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages/{name}/{version}/{target}":{"get":{"tags":["packages"],"operationId":"get_package_artifact","summary":"Describe one exact artifact","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"200":{"description":"The artifact and how to download it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artifact"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages/{name}/{version}/{target}/download":{"get":{"tags":["packages"],"operationId":"download_package","summary":"Stream the artifact bytes","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"200":{"description":"The artifact blob. `x-molto-checksum` carries its SHA-256 and `x-molto-yanked` whether it was withdrawn.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The blob is catalogued but unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages/{name}/{version}/{target}/blob":{"put":{"tags":["packages"],"operationId":"upload_package_blob","summary":"Upload the archive for one coordinate","description":"Streams the tar.zst into object storage, which verifies it hashes to `x-molto-checksum` and rejects it if it does not. The artifact stays invisible until the metadata is posted.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"},{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Lowercase hex SHA-256 the uploaded blob must match"},"required":true,"description":"Lowercase hex SHA-256 the uploaded blob must match","name":"x-molto-checksum","in":"header"}],"requestBody":{"description":"The archive bytes","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"201":{"description":"The blob was stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlobUpload"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That coordinate is already published, and they are immutable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages/{name}/{version}/{target}/yank":{"post":{"tags":["packages"],"operationId":"yank_package","summary":"Withdraw a version from new resolutions","description":"Marks the coordinate so that resolutions starting now do not choose it. The bytes stay, the download keeps working and carries `x-molto-yanked`, and a lock file that already names this version keeps building. Coordinates are immutable, so this is the only remedy for a publish that should not have happened. Idempotent.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"204":{"description":"The artifact is now yanked"},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such coordinate in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/packages/{name}/{version}/{target}/unyank":{"post":{"tags":["packages"],"operationId":"unyank_package","summary":"Put a withdrawn version back","description":"Clears the withdrawal, making the version eligible again. Idempotent.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"204":{"description":"The artifact is now unyanked"},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such coordinate in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools":{"get":{"tags":["tools"],"operationId":"list_tools","summary":"The catalogue index of published tools","description":"One entry per name, each with its latest version and the targets that version was built for — enough to tell whether this registry serves your platform without asking again per name.","responses":{"200":{"description":"The catalogue index","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogueList"}}}}}},"post":{"tags":["tools"],"operationId":"publish_tool","summary":"Record a tool whose blob is already uploaded","description":"The body is the `recipe.toml`, verbatim: it carries the coordinate and is stored whole as the artifact's metadata. The checksum and the size are measured from the stored blob, never declared, so a recipe cannot describe an archive other than the one that was uploaded. Written last on purpose: a catalogue entry without its blob is the one inconsistency the API cannot serve around. A recipe declaring `form = \"source\"` is the exception and the whole request: it has no blob anywhere, so nothing is uploaded first and nothing is measured.","requestBody":{"description":"The recipe.toml describing exactly one coordinate","content":{"application/toml":{"schema":{"type":"string"}}}},"responses":{"201":{"description":"The artifact is now in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artifact"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No blob has been uploaded for that coordinate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That coordinate is already published, and they are immutable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools/{name}":{"get":{"tags":["tools"],"operationId":"list_tool_releases","summary":"Every release published under one tool name","description":"Each release carries its targets in full: format, size, checksum and download URL. Resolving a name to something downloadable is this one request.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"Releases, most recently published first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseList"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools/{name}/{version}":{"get":{"tags":["tools"],"operationId":"get_tool_release","summary":"List every target built for one version","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"}],"responses":{"200":{"description":"The release and its targets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools/{name}/{version}/{target}":{"get":{"tags":["tools"],"operationId":"get_tool_artifact","summary":"Describe one exact artifact","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"200":{"description":"The artifact and how to download it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artifact"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools/{name}/{version}/{target}/download":{"get":{"tags":["tools"],"operationId":"download_tool","summary":"Stream the artifact bytes","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"200":{"description":"The artifact blob. `x-molto-checksum` carries its SHA-256 and `x-molto-yanked` whether it was withdrawn.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such artifact","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The blob is catalogued but unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools/{name}/{version}/{target}/blob":{"put":{"tags":["tools"],"operationId":"upload_tool_blob","summary":"Upload the archive for one coordinate","description":"Streams the tar.zst into object storage, which verifies it hashes to `x-molto-checksum` and rejects it if it does not. The artifact stays invisible until the metadata is posted.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"},{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Lowercase hex SHA-256 the uploaded blob must match"},"required":true,"description":"Lowercase hex SHA-256 the uploaded blob must match","name":"x-molto-checksum","in":"header"}],"requestBody":{"description":"The archive bytes","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"201":{"description":"The blob was stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlobUpload"}}}},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That coordinate is already published, and they are immutable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools/{name}/{version}/{target}/yank":{"post":{"tags":["tools"],"operationId":"yank_tool","summary":"Withdraw a version from new resolutions","description":"Marks the coordinate so that resolutions starting now do not choose it. The bytes stay, the download keeps working and carries `x-molto-yanked`, and a lock file that already names this version keeps building. Coordinates are immutable, so this is the only remedy for a publish that should not have happened. Idempotent.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"204":{"description":"The artifact is now yanked"},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such coordinate in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tools/{name}/{version}/{target}/unyank":{"post":{"tags":["tools"],"operationId":"unyank_tool","summary":"Put a withdrawn version back","description":"Clears the withdrawal, making the version eligible again. Idempotent.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$","example":"clang"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+-]*$","example":"19.1.0"},"required":true,"name":"version","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]*$","example":"linux-x86_64"},"required":true,"name":"target","in":"path"}],"responses":{"204":{"description":"The artifact is now unyanked"},"400":{"description":"The request did not satisfy the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No usable bearer token, or publishing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such coordinate in the catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{}}