Overview
Pull Docker images from public or private registries. Supports automatic vulnerability scanning after pull completion and real-time progress streaming via Server-Sent Events.
Endpoint
POST /api/images/pull?env={environmentId}
Query Parameters
Environment ID where the image will be pulled. Optional for local environments.
Request Body
Image name with optional tag or digest:
nginx:latest
ubuntu:22.04
registry.example.com/app:v1.0
nginx@sha256:abc123...
Run vulnerability scan after successful pull. Set to false to skip scan-on-pull when caller will handle scanning separately.
Authentication
Requires images:pull permission for the specified environment.
Registry Authentication
Authentication headers are automatically built using configured registry credentials. Private registries must be configured in Dockhand settings.
const authHeaders = await buildRegistryAuthHeader(image);
Returns a job ID for progress tracking via SSE:
{
"jobId": "550e8400-e29b-41d4-a716-446655440000"
}
Connect to the SSE endpoint to receive progress updates:
Progress Events
Download Progress
{
"id": "abc123",
"status": "Downloading",
"progressDetail": {
"current": 1048576,
"total": 10485760
},
"progress": "[====> ] 1MB/10MB"
}
{
"id": "def456",
"status": "Extracting",
"progressDetail": {
"current": 5242880,
"total": 10485760
}
}
Scan Progress (if enabled)
{
"status": "scanning",
"message": "Starting vulnerability scan..."
}
{
"status": "scan-progress",
"stage": "analyzing",
"message": "Analyzing image layers",
"progress": 45
}
Scan Results
{
"status": "scan-complete",
"message": "Scan complete - found 12 vulnerabilities",
"results": [
{
"imageId": "sha256:abc123...",
"imageName": "nginx:latest",
"scanner": "grype",
"scannedAt": "2024-03-04T10:30:00Z",
"scanDuration": 8500,
"summary": {
"critical": 0,
"high": 2,
"medium": 5,
"low": 5,
"negligible": 0,
"unknown": 0
},
"vulnerabilities": [
{
"id": "CVE-2024-1234",
"severity": "high",
"package": "openssl",
"version": "1.1.1",
"fixedVersion": "1.1.1w",
"description": "Buffer overflow vulnerability"
}
]
}
]
}
Complete
Error
{
"status": "error",
"error": "Image not found or registry authentication failed"
}
Error Responses
Permission denied{ "error": "Permission denied" }
Edge agent not connected{ "status": "error", "error": "Edge agent not connected" }
Edge Mode Support
For Hawser Edge environments, pull operations are proxied through the edge agent:
if (edgeCheck.isEdge && edgeCheck.environmentId) {
if (!isEdgeConnected(edgeCheck.environmentId)) {
sendData({ status: 'error', error: 'Edge agent not connected' });
return;
}
const pullUrl = buildPullUrl(image);
const authHeaders = await buildRegistryAuthHeader(image);
await sendEdgeStreamRequest(
edgeCheck.environmentId,
'POST',
pullUrl,
{ onData, onEnd, onError },
undefined,
authHeaders
);
}
Tag Parsing
The endpoint intelligently parses image names with tags and digests:
function buildPullUrl(imageName: string): string {
let fromImage = imageName;
let tag = 'latest';
if (imageName.includes('@')) {
// Digest format: nginx@sha256:abc123
fromImage = imageName;
tag = '';
} else if (imageName.includes(':')) {
// Tag format: nginx:1.21.0
const lastColonIndex = imageName.lastIndexOf(':');
const potentialTag = imageName.substring(lastColonIndex + 1);
if (!potentialTag.includes('/')) {
fromImage = imageName.substring(0, lastColonIndex);
tag = potentialTag;
}
}
return tag
? `/images/create?fromImage=${encodeURIComponent(fromImage)}&tag=${encodeURIComponent(tag)}`
: `/images/create?fromImage=${encodeURIComponent(fromImage)}`;
}
Scan-on-Pull
Automatic vulnerability scanning after pull (if scanner is configured):
const handleScanOnPull = async () => {
if (skipScanOnPull) return;
const { scanner } = await getScannerSettings(envId);
if (scanner !== 'none') {
sendData({ status: 'scanning', message: 'Starting vulnerability scan...' });
const results = await scanImage(image, envId, (progress) => {
sendData({ status: 'scan-progress', ...progress });
});
for (const result of results) {
await saveVulnerabilityScan({
environmentId: envId ?? null,
imageId: result.imageId,
imageName: result.imageName,
scanner: result.scanner,
vulnerabilities: result.vulnerabilities,
// ... summary counts
});
}
}
};
Usage Examples
Pull Public Image
curl -X POST 'https://dockhand.example.com/api/images/pull?env=1' \
-H 'Content-Type: application/json' \
-H 'Cookie: session=...' \
-d '{
"image": "nginx:latest",
"scanAfterPull": true
}'
Pull Private Registry Image
curl -X POST 'https://dockhand.example.com/api/images/pull?env=1' \
-H 'Content-Type: application/json' \
-d '{
"image": "registry.company.com/app:v2.1.0"
}'
Pull with Digest
curl -X POST 'https://dockhand.example.com/api/images/pull' \
-H 'Content-Type: application/json' \
-d '{
"image": "nginx@sha256:abc123def456..."
}'
Stream Progress (JavaScript)
const response = await fetch('/api/images/pull?env=1', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image: 'nginx:latest' })
});
const { jobId } = await response.json();
const eventSource = new EventSource(`/api/jobs/${jobId}`);
eventSource.addEventListener('progress', (e) => {
const data = JSON.parse(e.data);
console.log('Progress:', data.status, data.message);
});
eventSource.addEventListener('result', (e) => {
const data = JSON.parse(e.data);
console.log('Complete:', data);
eventSource.close();
});
Audit Logging
Pull operations are automatically logged:
await auditImage(event, 'pull', image, image, envId);
Notes
- Supports both local and edge environments via Hawser
- Registry authentication is automatic based on configured registries
- Scan-on-pull respects environment scanner settings
- Progress events are streamed in real-time via Server-Sent Events
- Tag defaults to
latest if not specified
- Digest format bypasses tag resolution