# 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
| `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):
| `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>`:
| `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:
| `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.