Files
at-container-registry/docs/HOLD_ENDPOINT_TESTS.md
T

5.3 KiB

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