175 lines
5.2 KiB
Markdown
175 lines
5.2 KiB
Markdown
# @push.rocks/smartregistry
|
|
|
|
A TypeScript library implementing the OCI Distribution Specification v1.1 for building container and artifact registries.
|
|
|
|
## Features
|
|
|
|
- **OCI Distribution Spec v1.1 Compliant**: Implements all required and optional endpoints
|
|
- **Cloud-Agnostic Storage**: Uses @push.rocks/smartbucket for S3-compatible object storage
|
|
- **Pluggable Authentication**: Async callbacks for login and authorization
|
|
- **Bearer Token Auth**: JWT-based authentication following Docker Registry Token Authentication spec
|
|
- **Programmatic API**: Use as a library in any Node.js/TypeScript application
|
|
- **Full CRUD Operations**: Push, pull, list, and delete manifests and blobs
|
|
- **Content Discovery**: Tag listing and referrers API for artifact relationships
|
|
- **Chunked Uploads**: Support for large blob uploads with resumable sessions
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install @push.rocks/smartregistry
|
|
# or
|
|
pnpm add @push.rocks/smartregistry
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Basic Setup
|
|
|
|
```typescript
|
|
import { SmartRegistry, IRegistryConfig, TLoginCallback, TAuthCallback } from '@push.rocks/smartregistry';
|
|
|
|
// Implement login callback
|
|
const loginCallback: TLoginCallback = async (credentials) => {
|
|
// Validate credentials and return JWT token
|
|
// This should create a proper JWT with required claims
|
|
return generateJWT(credentials.username);
|
|
};
|
|
|
|
// Implement authorization callback
|
|
const authCallback: TAuthCallback = async (token, repository, action) => {
|
|
// Validate token and check permissions
|
|
const claims = verifyJWT(token);
|
|
return hasPermission(claims, repository, action);
|
|
};
|
|
|
|
// Configure registry
|
|
const config: IRegistryConfig = {
|
|
storage: {
|
|
accessKey: 'your-s3-access-key',
|
|
accessSecret: 'your-s3-secret',
|
|
endpoint: 's3.amazonaws.com',
|
|
port: 443,
|
|
useSsl: true,
|
|
region: 'us-east-1',
|
|
bucketName: 'my-registry',
|
|
},
|
|
serviceName: 'my-registry',
|
|
tokenRealm: 'https://auth.example.com/token',
|
|
loginCallback,
|
|
authCallback,
|
|
};
|
|
|
|
// Create and initialize registry
|
|
const registry = new SmartRegistry(config);
|
|
await registry.init();
|
|
```
|
|
|
|
### Integration with HTTP Server
|
|
|
|
```typescript
|
|
import express from 'express';
|
|
|
|
const app = express();
|
|
|
|
// OCI Distribution API endpoints
|
|
app.get('/v2/', (req, res) => {
|
|
res.status(200).json({});
|
|
});
|
|
|
|
app.get('/v2/:name(*)/manifests/:reference', async (req, res) => {
|
|
const { name, reference } = req.params;
|
|
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
|
|
const result = await registry.getManifest(name, reference, token);
|
|
|
|
if ('errors' in result) {
|
|
return res.status(404).json(result);
|
|
}
|
|
|
|
res.setHeader('Content-Type', result.contentType);
|
|
res.setHeader('Docker-Content-Digest', result.digest);
|
|
res.send(result.data);
|
|
});
|
|
|
|
app.get('/v2/:name(*)/blobs/:digest', async (req, res) => {
|
|
const { name, digest } = req.params;
|
|
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
|
|
const result = await registry.getBlob(name, digest, token);
|
|
|
|
if ('errors' in result) {
|
|
return res.status(404).json(result);
|
|
}
|
|
|
|
res.setHeader('Content-Type', 'application/octet-stream');
|
|
res.send(result.data);
|
|
});
|
|
|
|
// ... implement other endpoints
|
|
|
|
app.listen(5000);
|
|
```
|
|
|
|
### Authentication Flow
|
|
|
|
```typescript
|
|
// Client requests without token
|
|
const challenge = registry.getAuthChallenge('library/nginx', ['pull', 'push']);
|
|
// Returns: Bearer realm="https://auth.example.com/token",service="my-registry",scope="repository:library/nginx:pull,push"
|
|
|
|
// Client authenticates
|
|
const token = await registry.login({ username: 'user', password: 'pass' });
|
|
|
|
// Client uses token for subsequent requests
|
|
const manifest = await registry.getManifest('library/nginx', 'latest', token);
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### Pull Operations (Required)
|
|
|
|
- `getManifest(repository, reference, token?)` - Download a manifest
|
|
- `headManifest(repository, reference, token?)` - Check manifest existence
|
|
- `getBlob(repository, digest, token?, range?)` - Download a blob
|
|
- `headBlob(repository, digest, token?)` - Check blob existence
|
|
|
|
### Push Operations
|
|
|
|
- `initiateUpload(repository, token, mountDigest?, fromRepository?)` - Start blob upload
|
|
- `uploadChunk(uploadId, data, contentRange, token)` - Upload blob chunk
|
|
- `completeUpload(uploadId, digest, token, finalData?)` - Finalize blob upload
|
|
- `putManifest(repository, reference, manifest, contentType, token)` - Upload manifest
|
|
|
|
### Content Discovery
|
|
|
|
- `listTags(repository, token?, pagination?)` - List all tags
|
|
- `getReferrers(repository, digest, token?, artifactType?)` - Get referencing artifacts
|
|
|
|
### Content Management
|
|
|
|
- `deleteManifest(repository, digest, token)` - Delete manifest
|
|
- `deleteBlob(repository, digest, token)` - Delete blob
|
|
- `deleteTag(repository, tag, token)` - Delete tag
|
|
|
|
### Authentication
|
|
|
|
- `login(credentials)` - Get authentication token
|
|
- `getAuthChallenge(repository, actions)` - Generate WWW-Authenticate header
|
|
|
|
## OCI Specification Compliance
|
|
|
|
This library implements:
|
|
|
|
- **Pull Category** (required): All manifest and blob retrieval operations
|
|
- **Push Category**: Complete blob upload workflow with chunked and monolithic modes
|
|
- **Content Discovery**: Tag listing and referrers API
|
|
- **Content Management**: Deletion operations for manifests, blobs, and tags
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
## Contributing
|
|
|
|
See the main repository for contribution guidelines.
|