A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
80
fork

Configure Feed

Select the types of activity you want to include in your feed.

try new permission sets

+74 -36
+27
lexicons/io/atcr/authFullApp.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "io.atcr.authFullApp", 4 + "defs": { 5 + "main": { 6 + "type": "permission-set", 7 + "title": "AT Container Registry", 8 + "title:langs": {}, 9 + "detail": "Push and pull container images to the ATProto Container Registry. Includes creating and managing image manifests, tags, and repository settings.", 10 + "detail:langs": {}, 11 + "permissions": [ 12 + { 13 + "type": "permission", 14 + "resource": "repo", 15 + "action": ["create", "update", "delete"], 16 + "collection": ["io.atcr.manifest", "io.atcr.tag", "io.atcr.sailor.star", "io.atcr.sailor.profile", "io.atcr.repo.page"] 17 + }, 18 + { 19 + "type": "permission", 20 + "resource": "rpc", 21 + "lxm": ["com.atproto.repo.getRecord"], 22 + "aud": "*" 23 + } 24 + ] 25 + } 26 + } 27 + }
+4 -2
lexicons/io/atcr/hold/captain.json
··· 34 34 }, 35 35 "region": { 36 36 "type": "string", 37 - "description": "S3 region where blobs are stored" 37 + "description": "S3 region where blobs are stored", 38 + "maxLength": 64 38 39 }, 39 40 "provider": { 40 41 "type": "string", 41 - "description": "Deployment provider (e.g., fly.io, aws, etc.)" 42 + "description": "Deployment provider (e.g., fly.io, aws, etc.)", 43 + "maxLength": 64 42 44 } 43 45 } 44 46 }
+4 -2
lexicons/io/atcr/hold/crew.json
··· 18 18 "role": { 19 19 "type": "string", 20 20 "description": "Member's role in the hold", 21 - "knownValues": ["owner", "admin", "write", "read"] 21 + "knownValues": ["owner", "admin", "write", "read"], 22 + "maxLength": 32 22 23 }, 23 24 "permissions": { 24 25 "type": "array", 25 26 "description": "Specific permissions granted to this member", 26 27 "items": { 27 - "type": "string" 28 + "type": "string", 29 + "maxLength": 64 28 30 } 29 31 }, 30 32 "addedAt": {
+6 -3
lexicons/io/atcr/hold/layer.json
··· 12 12 "properties": { 13 13 "digest": { 14 14 "type": "string", 15 - "description": "Layer digest (e.g., sha256:abc123...)" 15 + "description": "Layer digest (e.g., sha256:abc123...)", 16 + "maxLength": 128 16 17 }, 17 18 "size": { 18 19 "type": "integer", ··· 20 21 }, 21 22 "mediaType": { 22 23 "type": "string", 23 - "description": "Media type (e.g., application/vnd.oci.image.layer.v1.tar+gzip)" 24 + "description": "Media type (e.g., application/vnd.oci.image.layer.v1.tar+gzip)", 25 + "maxLength": 128 24 26 }, 25 27 "repository": { 26 28 "type": "string", 27 - "description": "Repository this layer belongs to" 29 + "description": "Repository this layer belongs to", 30 + "maxLength": 255 28 31 }, 29 32 "userDid": { 30 33 "type": "string",
+22 -11
lexicons/io/atcr/manifest.json
··· 17 17 }, 18 18 "digest": { 19 19 "type": "string", 20 - "description": "Content digest (e.g., 'sha256:abc123...')" 20 + "description": "Content digest (e.g., 'sha256:abc123...')", 21 + "maxLength": 128 21 22 }, 22 23 "holdDid": { 23 24 "type": "string", ··· 37 38 "application/vnd.docker.distribution.manifest.v2+json", 38 39 "application/vnd.oci.image.index.v1+json", 39 40 "application/vnd.docker.distribution.manifest.list.v2+json" 40 - ] 41 + ], 42 + "maxLength": 128 41 43 }, 42 44 "schemaVersion": { 43 45 "type": "integer", ··· 92 94 "properties": { 93 95 "mediaType": { 94 96 "type": "string", 95 - "description": "MIME type of the blob" 97 + "description": "MIME type of the blob", 98 + "maxLength": 128 96 99 }, 97 100 "size": { 98 101 "type": "integer", ··· 100 103 }, 101 104 "digest": { 102 105 "type": "string", 103 - "description": "Content digest (e.g., 'sha256:...')" 106 + "description": "Content digest (e.g., 'sha256:...')", 107 + "maxLength": 128 104 108 }, 105 109 "urls": { 106 110 "type": "array", ··· 123 127 "properties": { 124 128 "mediaType": { 125 129 "type": "string", 126 - "description": "Media type of the referenced manifest" 130 + "description": "Media type of the referenced manifest", 131 + "maxLength": 128 127 132 }, 128 133 "size": { 129 134 "type": "integer", ··· 131 136 }, 132 137 "digest": { 133 138 "type": "string", 134 - "description": "Content digest (e.g., 'sha256:...')" 139 + "description": "Content digest (e.g., 'sha256:...')", 140 + "maxLength": 128 135 141 }, 136 142 "platform": { 137 143 "type": "ref", ··· 151 157 "properties": { 152 158 "architecture": { 153 159 "type": "string", 154 - "description": "CPU architecture (e.g., 'amd64', 'arm64', 'arm')" 160 + "description": "CPU architecture (e.g., 'amd64', 'arm64', 'arm')", 161 + "maxLength": 32 155 162 }, 156 163 "os": { 157 164 "type": "string", 158 - "description": "Operating system (e.g., 'linux', 'windows', 'darwin')" 165 + "description": "Operating system (e.g., 'linux', 'windows', 'darwin')", 166 + "maxLength": 32 159 167 }, 160 168 "osVersion": { 161 169 "type": "string", 162 - "description": "Optional OS version" 170 + "description": "Optional OS version", 171 + "maxLength": 64 163 172 }, 164 173 "osFeatures": { 165 174 "type": "array", 166 175 "items": { 167 - "type": "string" 176 + "type": "string", 177 + "maxLength": 64 168 178 }, 169 179 "description": "Optional OS features" 170 180 }, 171 181 "variant": { 172 182 "type": "string", 173 - "description": "Optional CPU variant (e.g., 'v7' for ARM)" 183 + "description": "Optional CPU variant (e.g., 'v7' for ARM)", 184 + "maxLength": 32 174 185 } 175 186 } 176 187 }
+2 -1
lexicons/io/atcr/tag.json
··· 27 27 }, 28 28 "manifestDigest": { 29 29 "type": "string", 30 - "description": "DEPRECATED: Digest of the manifest (e.g., 'sha256:...'). Kept for backward compatibility with old records. New records should use 'manifest' field instead." 30 + "description": "DEPRECATED: Digest of the manifest (e.g., 'sha256:...'). Kept for backward compatibility with old records. New records should use 'manifest' field instead.", 31 + "maxLength": 128 31 32 }, 32 33 "createdAt": { 33 34 "type": "string",
+9 -17
pkg/auth/oauth/client.go
··· 72 72 return baseURL + "/auth/oauth/callback" 73 73 } 74 74 75 - // GetDefaultScopes returns the default OAuth scopes for ATCR registry operations 76 - // testMode determines whether to use transition:generic (test) or rpc scopes (production) 75 + // GetDefaultScopes returns the default OAuth scopes for ATCR registry operations. 76 + // Uses io.atcr.permissions#registry permission-set to bundle repo and rpc scopes. 77 + // Blob scopes are listed explicitly (not supported in Lexicon permission-sets). 77 78 func GetDefaultScopes(did string) []string { 78 - scopes := []string{ 79 + return []string{ 79 80 "atproto", 80 - // Used for service token validation on holds 81 - "rpc:com.atproto.repo.getRecord?aud=*", 81 + // Permission-set bundles repo and rpc scopes 82 + // See lexicons/io/atcr/authFullApp.json for definition 83 + "io.atcr.authFullApp", 84 + // Blob scopes (not supported in Lexicon permission-sets) 82 85 // Image manifest types (single-arch) 83 86 "blob:application/vnd.oci.image.manifest.v1+json", 84 87 "blob:application/vnd.docker.distribution.manifest.v2+json", ··· 87 90 "blob:application/vnd.docker.distribution.manifest.list.v2+json", 88 91 // OCI artifact manifests (for cosign signatures, SBOMs, attestations) 89 92 "blob:application/vnd.cncf.oras.artifact.manifest.v1+json", 90 - // image avatars 93 + // Image avatars 91 94 "blob:image/*", 92 95 } 93 - 94 - // Add repo scopes 95 - scopes = append(scopes, 96 - fmt.Sprintf("repo:%s", atproto.ManifestCollection), 97 - fmt.Sprintf("repo:%s", atproto.TagCollection), 98 - fmt.Sprintf("repo:%s", atproto.StarCollection), 99 - fmt.Sprintf("repo:%s", atproto.SailorProfileCollection), 100 - fmt.Sprintf("repo:%s", atproto.RepoPageCollection), 101 - ) 102 - 103 - return scopes 104 96 } 105 97 106 98 // ScopesMatch checks if two scope lists are equivalent (order-independent)