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

Configure Feed

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

Hold Service Endpoint Testing Guide#

Quick Reference#

Your hold service: http://172.28.0.3:8080

Default DID format for local testing: did:web:172.28.0.3%3A8080 (URL-encoded did:web:172.28.0.3:8080)

Individual cURL Commands#

1. List Repositories#

curl -s "http://172.28.0.3:8080/xrpc/com.atproto.sync.listRepos" | jq .

Expected response:

{
  "repos": [
    {
      "did": "did:web:172.28.0.3%3A8080",
      "head": "...",
      "rev": "..."
    }
  ]
}

2. Describe Repository#

curl -s "http://172.28.0.3:8080/xrpc/com.atproto.repo.describeRepo?repo=did:web:172.28.0.3%3A8080" | jq .

Expected response:

{
  "did": "did:web:172.28.0.3%3A8080",
  "handle": "172.28.0.3:8080",
  "didDoc": {...},
  "collections": ["io.atcr.hold.captain", "io.atcr.hold.crew"]
}

3. Get Repository (CAR file)#

# Download entire repo as CAR file
curl -s "http://172.28.0.3:8080/xrpc/com.atproto.sync.getRepo?did=did:web:172.28.0.3%3A8080" -o repo.car

# Get repo diff since revision
curl -s "http://172.28.0.3:8080/xrpc/com.atproto.sync.getRepo?did=did:web:172.28.0.3%3A8080&since=abc123" -o repo-diff.car

Expected response: Binary CAR (Content Addressable aRchive) file

4. List Captain Records#

curl -s "http://172.28.0.3:8080/xrpc/com.atproto.repo.listRecords?repo=did:web:172.28.0.3%3A8080&collection=io.atcr.hold.captain" | jq .

Expected response:

{
  "records": [
    {
      "uri": "at://did:web:172.28.0.3%3A8080/io.atcr.hold.captain/self",
      "cid": "...",
      "value": {
        "$type": "io.atcr.hold.captain",
        "allowAllCrew": true,
        "public": false,
        "createdAt": "2025-10-22T..."
      }
    }
  ]
}

5. List Crew Records#

curl -s "http://172.28.0.3:8080/xrpc/com.atproto.repo.listRecords?repo=did:web:172.28.0.3%3A8080&collection=io.atcr.hold.crew" | jq .

Expected response:

{
  "records": [
    {
      "uri": "at://did:web:172.28.0.3%3A8080/io.atcr.hold.crew/{rkey}",
      "cid": "...",
      "value": {
        "$type": "io.atcr.hold.crew",
        "did": "did:plc:...",
        "permissions": ["blob:read", "blob:write"],
        "createdAt": "2025-10-22T..."
      }
    }
  ]
}

6. Get Specific Record#

curl -s "http://172.28.0.3:8080/xrpc/com.atproto.repo.getRecord?repo=did:web:172.28.0.3%3A8080&collection=io.atcr.hold.captain&rkey=self" | jq .

7. Get Blob#

# Replace with actual CID from your hold
curl -s "http://172.28.0.3:8080/xrpc/com.atproto.sync.getBlob?did=did:web:172.28.0.3%3A8080&cid=bafyreiabc123..." | jq .

Expected response (for OCI blobs):

{
  "url": "https://s3.amazonaws.com/bucket/path?presigned-params...",
  "expiresAt": "2025-10-22T12:15:00Z"
}

8. Subscribe to Repository Events (WebSocket)#

Using websocat (recommended):

# Install: cargo install websocat
websocat "ws://172.28.0.3:8080/xrpc/com.atproto.sync.subscribeRepos"

Using wscat:

# Install: npm install -g wscat
wscat -c "ws://172.28.0.3:8080/xrpc/com.atproto.sync.subscribeRepos"

Using curl (HTTP upgrade - may not work with all servers):

curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: $(echo -n "test" | base64)" \
  "http://172.28.0.3:8080/xrpc/com.atproto.sync.subscribeRepos"

Expected response: Stream of CBOR-encoded events (commits, identities, handles, etc.)

DID Resolution#

Get DID Document#

curl -s "http://172.28.0.3:8080/.well-known/did.json" | jq .

Expected response:

{
  "@context": ["https://www.w3.org/ns/did/v1"],
  "id": "did:web:172.28.0.3%3A8080",
  "service": [
    {
      "id": "#atproto_pds",
      "type": "AtprotoPersonalDataServer",
      "serviceEndpoint": "http://172.28.0.3:8080"
    }
  ]
}

Get DID from Handle#

curl -s "http://172.28.0.3:8080/.well-known/atproto-did"

Expected response: Plain text DID

did:web:172.28.0.3%3A8080

Running the Test Script#

# Default (uses 172.28.0.3:8080)
./test-hold-endpoints.sh

# Custom hold URL
./test-hold-endpoints.sh "http://localhost:8080"

# Custom hold URL and DID
./test-hold-endpoints.sh "http://localhost:8080" "did:web:localhost%3A8080"

Troubleshooting#

"Connection refused"#

  • Ensure hold service is running: docker ps or check process
  • Verify IP address: docker inspect <container> | grep IPAddress

"Empty response" or "404 Not Found"#

  • Check hold service logs for errors
  • Verify DID format (use URL-encoded version with %3A for :)
  • Ensure hold has been initialized (should have captain record)

WebSocket connection fails#

  • Install websocat: cargo install websocat
  • Or install wscat: npm install -g wscat
  • WebSocket endpoints only work with proper WS clients, not regular curl

"No records found"#

  • Captain record created on hold startup if HOLD_OWNER is set
  • Crew records created when users call io.atcr.hold.requestCrew
  • Blobs only exist after pushing container images

Next Steps#

After verifying these endpoints work:

  1. Test OCI upload endpoints (requires authentication)
  2. Push a real container image to create blob data
  3. Test blob retrieval with real CIDs
  4. Monitor WebSocket events during pushes