A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
atcr.io
docker
container
atproto
go
1# Hold Service Endpoint Testing Guide
2
3## Quick Reference
4
5Your hold service: `http://172.28.0.3:8080`
6
7Default DID format for local testing: `did:web:172.28.0.3%3A8080` (URL-encoded `did:web:172.28.0.3:8080`)
8
9## Individual cURL Commands
10
11### 1. List Repositories
12```bash
13curl -s "http://172.28.0.3:8080/xrpc/com.atproto.sync.listRepos" | jq .
14```
15
16**Expected response:**
17```json
18{
19 "repos": [
20 {
21 "did": "did:web:172.28.0.3%3A8080",
22 "head": "...",
23 "rev": "..."
24 }
25 ]
26}
27```
28
29### 2. Describe Repository
30```bash
31curl -s "http://172.28.0.3:8080/xrpc/com.atproto.repo.describeRepo?repo=did:web:172.28.0.3%3A8080" | jq .
32```
33
34**Expected response:**
35```json
36{
37 "did": "did:web:172.28.0.3%3A8080",
38 "handle": "172.28.0.3:8080",
39 "didDoc": {...},
40 "collections": ["io.atcr.hold.captain", "io.atcr.hold.crew"]
41}
42```
43
44### 3. Get Repository (CAR file)
45```bash
46# Download entire repo as CAR file
47curl -s "http://172.28.0.3:8080/xrpc/com.atproto.sync.getRepo?did=did:web:172.28.0.3%3A8080" -o repo.car
48
49# Get repo diff since revision
50curl -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
51```
52
53**Expected response:** Binary CAR (Content Addressable aRchive) file
54
55### 4. List Captain Records
56```bash
57curl -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 .
58```
59
60**Expected response:**
61```json
62{
63 "records": [
64 {
65 "uri": "at://did:web:172.28.0.3%3A8080/io.atcr.hold.captain/self",
66 "cid": "...",
67 "value": {
68 "$type": "io.atcr.hold.captain",
69 "allowAllCrew": true,
70 "public": false,
71 "createdAt": "2025-10-22T..."
72 }
73 }
74 ]
75}
76```
77
78### 5. List Crew Records
79```bash
80curl -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 .
81```
82
83**Expected response:**
84```json
85{
86 "records": [
87 {
88 "uri": "at://did:web:172.28.0.3%3A8080/io.atcr.hold.crew/{rkey}",
89 "cid": "...",
90 "value": {
91 "$type": "io.atcr.hold.crew",
92 "did": "did:plc:...",
93 "permissions": ["blob:read", "blob:write"],
94 "createdAt": "2025-10-22T..."
95 }
96 }
97 ]
98}
99```
100
101### 6. Get Specific Record
102```bash
103curl -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 .
104```
105
106### 7. Get Blob
107```bash
108# Replace with actual CID from your hold
109curl -s "http://172.28.0.3:8080/xrpc/com.atproto.sync.getBlob?did=did:web:172.28.0.3%3A8080&cid=bafyreiabc123..." | jq .
110```
111
112**Expected response (for OCI blobs):**
113```json
114{
115 "url": "https://s3.amazonaws.com/bucket/path?presigned-params...",
116 "expiresAt": "2025-10-22T12:15:00Z"
117}
118```
119
120### 8. Subscribe to Repository Events (WebSocket)
121
122Using **websocat** (recommended):
123```bash
124# Install: cargo install websocat
125websocat "ws://172.28.0.3:8080/xrpc/com.atproto.sync.subscribeRepos"
126```
127
128Using **wscat**:
129```bash
130# Install: npm install -g wscat
131wscat -c "ws://172.28.0.3:8080/xrpc/com.atproto.sync.subscribeRepos"
132```
133
134Using **curl** (HTTP upgrade - may not work with all servers):
135```bash
136curl -i -N \
137 -H "Connection: Upgrade" \
138 -H "Upgrade: websocket" \
139 -H "Sec-WebSocket-Version: 13" \
140 -H "Sec-WebSocket-Key: $(echo -n "test" | base64)" \
141 "http://172.28.0.3:8080/xrpc/com.atproto.sync.subscribeRepos"
142```
143
144**Expected response:** Stream of CBOR-encoded events (commits, identities, handles, etc.)
145
146## DID Resolution
147
148### Get DID Document
149```bash
150curl -s "http://172.28.0.3:8080/.well-known/did.json" | jq .
151```
152
153**Expected response:**
154```json
155{
156 "@context": ["https://www.w3.org/ns/did/v1"],
157 "id": "did:web:172.28.0.3%3A8080",
158 "service": [
159 {
160 "id": "#atproto_pds",
161 "type": "AtprotoPersonalDataServer",
162 "serviceEndpoint": "http://172.28.0.3:8080"
163 }
164 ]
165}
166```
167
168### Get DID from Handle
169```bash
170curl -s "http://172.28.0.3:8080/.well-known/atproto-did"
171```
172
173**Expected response:** Plain text DID
174```
175did:web:172.28.0.3%3A8080
176```
177
178## Running the Test Script
179
180```bash
181# Default (uses 172.28.0.3:8080)
182./test-hold-endpoints.sh
183
184# Custom hold URL
185./test-hold-endpoints.sh "http://localhost:8080"
186
187# Custom hold URL and DID
188./test-hold-endpoints.sh "http://localhost:8080" "did:web:localhost%3A8080"
189```
190
191## Troubleshooting
192
193### "Connection refused"
194- Ensure hold service is running: `docker ps` or check process
195- Verify IP address: `docker inspect <container> | grep IPAddress`
196
197### "Empty response" or "404 Not Found"
198- Check hold service logs for errors
199- Verify DID format (use URL-encoded version with `%3A` for `:`)
200- Ensure hold has been initialized (should have captain record)
201
202### WebSocket connection fails
203- Install websocat: `cargo install websocat`
204- Or install wscat: `npm install -g wscat`
205- WebSocket endpoints only work with proper WS clients, not regular curl
206
207### "No records found"
208- Captain record created on hold startup if `HOLD_OWNER` is set
209- Crew records created when users call `io.atcr.hold.requestCrew`
210- Blobs only exist after pushing container images
211
212## Next Steps
213
214After verifying these endpoints work:
2151. Test OCI upload endpoints (requires authentication)
2162. Push a real container image to create blob data
2173. Test blob retrieval with real CIDs
2184. Monitor WebSocket events during pushes