cellos-export-http 0.5.1

HTTP ExportSink for CellOS — POSTs per-cell evidence bundles to a configured webhook endpoint.
Documentation
# cellos-export-http

`ExportSink` that HTTP-PUTs evidence artifacts to a configurable base
URL — generic endpoints, CI artifact APIs, or presigned URLs that
aren't S3.

## What it is

Implements `cellos_core::ports::ExportSink`. For each pushed artifact,
the sink resolves an upload URL and PUTs the bytes via reqwest with
bounded request and connect timeouts, optionally re-trying on transient
errors with a fixed backoff.

URL resolution:

- If `base_url` contains `{cell_id}` or `{artifact_name}`, those
  placeholders are substituted.
- Else if `base_url` contains a query string, it is treated as an
  exact URL (e.g. a presigned URL).
- Else the sink appends `/{cell_id}/{artifact_name}`.

The base URL must parse as `http` or `https` after trim. Other schemes
and the empty string are rejected at construction.

Selected in `cellos-supervisor::composition::build_http_transport_sink`
as either:

- the **default** export sink when `CELLOS_EXPORT_HTTP_BASE_URL` is set
  and no cell-spec target overrides it; **or**
- a **named** target sink when the cell spec declares an HTTP target and
  the operator provides `CELLOS_EXPORT_HTTP_BASE_URL__<NAME>`.

What it does NOT do:

- It does not understand S3 — use `cellos-export-s3` for logical
  `s3://` receipts.
- It does not do multipart upload. One artifact, one PUT.
- It does not authenticate beyond an optional bearer token.

## Public API surface

| Symbol | Purpose |
|---|---|
| `HttpExportSink` | The sink. |
| `HttpExportSink::new(base_url, cell_id, bearer_token, max_attempts, retry_backoff_ms)` | Explicit constructor; rejects non-`http`/`https` URLs. |
| `HttpExportSink::from_env(cell_id)` | Convenience constructor that reads `CELLOS_EXPORT_HTTP_*` env vars. |
| `DEFAULT_REQUEST_TIMEOUT_MS` / `ENV_REQUEST_TIMEOUT_MS` | 30 s default request timeout; overridable. |
| `DEFAULT_CONNECT_TIMEOUT_MS` / `ENV_CONNECT_TIMEOUT_MS` | 10 s default connect timeout; overridable. |
| `resolve_timeout_ms(env_var, default_ms)` | Pure helper. |

Source: [`src/lib.rs`](src/lib.rs).

## Configuration

Default (no `__<NAME>` suffix):

| Env var | Description |
|---|---|
| `CELLOS_EXPORT_HTTP_BASE_URL` | Required to enable HTTP as the default export. |
| `CELLOS_EXPORT_HTTP_BEARER_TOKEN` | Optional `Authorization: Bearer <token>`. Zeroized on drop. |
| `CELLOS_EXPORT_HTTP_MAX_ATTEMPTS` | Total PUT attempts including the first (≥ 1; default 1). |
| `CELLOS_EXPORT_HTTP_RETRY_BACKOFF_MS` | Fixed delay between attempts (default 0). |

Per named target `<NAME>`:

| Env var |
|---|
| `CELLOS_EXPORT_HTTP_BASE_URL__<NAME>` |
| `CELLOS_EXPORT_HTTP_BEARER_TOKEN__<NAME>` |
| `CELLOS_EXPORT_HTTP_MAX_ATTEMPTS__<NAME>` |
| `CELLOS_EXPORT_HTTP_RETRY_BACKOFF_MS__<NAME>` |

Global:

| Env var | Default |
|---|---|
| `CELLOS_EXPORT_HTTP_TIMEOUT_MS` | 30 000 |
| `CELLOS_EXPORT_HTTP_CONNECT_TIMEOUT_MS` | 10 000 |
| `CELLOS_CA_BUNDLE` | PEM CA bundle path for private PKI. |
| `CELL_OS_REQUIRE_HTTP_EXPORT` | If set, missing base URL is a hard error instead of a noop fallback. |

`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` are honoured automatically.

## Examples

Default sink, S3-presigned PUT used via the generic HTTP path:

```bash
export CELLOS_EXPORT_HTTP_BASE_URL="https://artifacts.example.com/upload/{cell_id}/{artifact_name}"
export CELLOS_EXPORT_HTTP_BEARER_TOKEN="$(cat /run/secrets/artifacts-token)"
export CELLOS_EXPORT_HTTP_MAX_ATTEMPTS=3
export CELLOS_EXPORT_HTTP_RETRY_BACKOFF_MS=500
cellos-supervisor --spec cell.yaml
```

Named target `audit-bucket`:

```bash
export CELLOS_EXPORT_HTTP_BASE_URL__AUDIT_BUCKET="https://audit.example.com/{cell_id}/{artifact_name}"
```

## Testing

```bash
cargo test -p cellos-export-http
```

Tests exercise URL templating, retry semantics, the timeout-resolution
helper, and `from_env` construction without hitting a live endpoint.

## Related crates

- `cellos-export-local` — local-disk default.
- `cellos-export-s3` — S3-aware sibling that writes logical `s3://`
  receipts.
- `cellos-supervisor` — selects this sink in `build_http_transport_sink`.
- `cellos-core` — defines `ExportSink`, `ExportReceipt`,
  `ExportReceiptTargetKind`.

## Operator note: tracing

`reqwest` can emit any custom `Authorization` header configured for the
export endpoint at `TRACE` level. Every CellOS binary that initializes
tracing wires `cellos_core::observability::redacted_filter` into the fmt
layer so `RUST_LOG=reqwest=trace` cannot leak credentials to stderr. Do
not bypass this filter in custom tracing-init paths.

## ADRs

- ADR-0006 — evidence bundle is the 1.0 deliverable; HTTP PUT is the
  generic transport for export endpoints that are neither S3 nor local
  disk.