diff --git a/changelog.md b/changelog.md index 75bf38c..db62492 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-11-20 - 1.1.1 - fix(oci) +Improve OCI manifest permission response and tag handling: include WWW-Authenticate header on unauthorized manifest GETs, accept optional headers in manifest lookup, and persist tags as a unified tags.json mapping when pushing manifests. + +- getManifest now accepts an optional headers parameter for better request context handling. +- Unauthorized GET manifest responses now include a WWW-Authenticate header with realm/service/scope to comply with OCI auth expectations. +- PUT manifest logic no longer writes individual tag objects; it updates a consolidated oci/tags/{repository}/tags.json mapping using getTagsData and putObject. +- Simplified tag update flow when pushing a manifest: tags[reference] = digest and persist tags.json. + ## 2025-11-20 - 1.1.0 - feat(oci) Support monolithic OCI blob uploads; add registry cleanup/destroy hooks; update tests and docs diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index d90fbb7..4e67c25 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartregistry', - version: '1.1.0', + version: '1.1.1', description: 'a registry for npm modules and oci images' } diff --git a/ts/oci/classes.ociregistry.ts b/ts/oci/classes.ociregistry.ts index dae0929..56dfe50 100644 --- a/ts/oci/classes.ociregistry.ts +++ b/ts/oci/classes.ociregistry.ts @@ -284,12 +284,15 @@ export class OciRegistry extends BaseRegistry { private async getManifest( repository: string, reference: string, - token: IAuthToken | null + token: IAuthToken | null, + headers?: Record ): Promise { if (!await this.checkPermission(token, repository, 'pull')) { return { status: 401, - headers: {}, + headers: { + 'WWW-Authenticate': `Bearer realm="${this.basePath}/v2/token",service="registry",scope="repository:${repository}:pull"`, + }, body: this.createError('DENIED', 'Insufficient permissions'), }; } @@ -402,11 +405,12 @@ export class OciRegistry extends BaseRegistry { // Store manifest by digest await this.storage.putOciManifest(repository, digest, manifestData, contentType); - // If reference is a tag (not a digest), create tag reference + // If reference is a tag (not a digest), update tags mapping if (!reference.startsWith('sha256:')) { - // Store tag -> digest mapping - const tagPath = `oci/repositories/${repository}/tags/${reference}`; - await this.storage.putObject(tagPath, Buffer.from(digest, 'utf-8')); + const tags = await this.getTagsData(repository); + tags[reference] = digest; + const tagsPath = `oci/tags/${repository}/tags.json`; + await this.storage.putObject(tagsPath, Buffer.from(JSON.stringify(tags), 'utf-8')); } return {