polykit-cache 0.2.0

Self-hosted HTTP cache server for Polykit remote caching
Documentation
<div align="center">
  <h1>Polykit Cache Server</h1>
  
  [<img alt="github" src="https://img.shields.io/badge/github-jonathanmagambo/polykit-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/jonathanmagambo/polykit)
  [<img alt="crates.io" src="https://img.shields.io/crates/v/polykit-cache.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/polykit-cache)
  [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-polykit--cache-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/polykit-cache)
  [<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/jonathanmagambo/Polykit/rust.yml?branch=main&style=for-the-badge" height="20">](https://github.com/jonathanmagambo/Polykit/actions?query=branch%3Amain)
</div>

Self-hosted HTTP cache server for Polykit remote caching.

**Features:** Self-hosted, single binary, content-addressed storage, directory sharding, atomic operations, integrity verification, streaming I/O, graceful shutdown.

## Installation

Install from crates.io (recommended):

```bash
cargo install polykit-cache
```

Or build from source:

```bash
cargo build --release --package polykit-cache
```

Binary: `target/release/polykit-cache`

## Usage

### Starting the Server

Basic usage with defaults (port 8080, `./cache` storage):

```bash
polykit-cache
```

### Configuration Options

```bash
polykit-cache \
  --storage-dir /var/cache/polykit \
  --max-size 5368709120 \
  --bind 0.0.0.0 \
  --port 8080 \
  --log-level info
```

**Options:**
- `--storage-dir`: Directory for artifact storage (default: `./cache`)
- `--max-size`: Maximum artifact size in bytes (default: 1GB)
- `--bind`: Bind address (default: `127.0.0.1`)
- `--port`: Port number (default: `8080`)
- `--log-level`: Log level - trace, debug, info, warn, error (default: `info`)

### Client Configuration

**CLI:** `polykit build --remote-cache-url http://localhost:8080`

**Config file (`polykit.toml`):**
```toml
[remote_cache]
url = "http://localhost:8080"
```

## Deployment

### Docker

**Build:**
```bash
docker build -f polykit-cache/Dockerfile -t polykit-cache .
docker run -p 8080:8080 -v /path/to/cache:/var/cache/polykit polykit-cache
```

**Or use docker-compose:**
```bash
docker-compose -f polykit-cache/docker-compose.yml up -d
```

### Systemd Service

Create `/etc/systemd/system/polykit-cache.service`:

```ini
[Unit]
Description=Polykit Cache Server
After=network.target

[Service]
Type=simple
User=polykit
Group=polykit
ExecStart=/usr/local/bin/polykit-cache \
  --storage-dir /var/cache/polykit \
  --bind 0.0.0.0 \
  --port 8080 \
  --log-level info
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
```

Enable and start:

```bash
sudo systemctl enable polykit-cache
sudo systemctl start polykit-cache
```

### Reverse Proxy (Nginx)

For production deployments, use a reverse proxy:

```nginx
upstream polykit_cache {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name cache.example.com;

    client_max_body_size 1G;

    location / {
        proxy_pass http://polykit_cache;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

## API

### Upload Artifact

```http
PUT /v1/artifacts/{cache_key}
Content-Type: application/octet-stream

<zstd-compressed artifact data>
```

**Response:**
- `201 Created` - Upload successful
- `400 Bad Request` - Invalid cache key format
- `409 Conflict` - Artifact already exists
- `413 Payload Too Large` - Artifact exceeds size limit
- `422 Unprocessable Entity` - Verification failed

### Download Artifact

```http
GET /v1/artifacts/{cache_key}
```

**Response:**
- `200 OK` - Returns compressed artifact with headers:
  - `Content-Type: application/zstd`
  - `Content-Length: <size>`
  - `X-Artifact-Hash: <sha256>`
- `404 Not Found` - Artifact does not exist

### Check Artifact Existence

```http
HEAD /v1/artifacts/{cache_key}
```

**Response:**
- `200 OK` - Artifact exists (includes same headers as GET)
- `404 Not Found` - Artifact does not exist

## Storage Layout

Artifacts are stored with directory sharding:

```
<storage-dir>/
  aa/
    bb/
      <cache_key>.zst      # Compressed artifact
      <cache_key>.json     # Metadata
```

The first 4 characters of the cache key determine the directory structure (`aa/bb/`).

## Security

**No built-in auth** - Deploy behind firewall/VPN or use reverse proxy (Nginx/Caddy) with TLS and auth.

**Storage limits** - Set `--max-size` to prevent exhaustion. Run as dedicated user with restricted permissions.

## Monitoring

Logs to stdout. Monitor uploads/downloads, verification failures, storage errors, shutdown signals. Integrate with journald, fluentd, or Loki.

## Performance

**Concurrent uploads** - No global locks, scales with CPU cores. **Streaming** - No full buffering. **Atomic writes** - Temp file + rename. **Directory sharding** - Prevents filesystem slowdowns.

## License

See LICENSE.md in the repository root.