# API Roadmap This document records planned API work that is not part of the current implementation. Current HTTP upload behavior is documented in `docs/integrations/http-upload.md` and current producer package usage is documented under `docs/consumers/`. ## Pipeline-Scoped HTTP Upload API Current `http_upload` behavior uses one bearer token to both authenticate a producer and select exactly one pipeline. That is simple, but it does not scale well for producer applications that generate multiple report types on different schedules. Planned work: - Separate upload authentication from pipeline routing. - Add token records that can authorize one producer/client for one or more upload pipelines. - Add a pipeline-scoped upload endpoint: ```text POST /v1/pipelines/{pipeline_id}/upload ``` - Keep the source manifest free of routing, destination, transform, and credential data. - Keep `http_upload` source-only. A selected pipeline still owns destination configuration, transforms, links, transfer policy, and publication behavior. ## Proposed Configuration Shape Move bearer token configuration out of individual pipeline sources and into a top-level upload token list: ```yaml upload_tokens: - id: weatherreporter-prod token_env: WEATHERREPORTER_UPLOAD_TOKEN allow_pipelines: - weather.morning - weather.weekend - weather.next_6_hours - weather.storm - weather.event ``` Pipeline sources would continue to use `http_upload`, but would no longer need one unique token per pipeline: ```yaml pipelines: - id: weather.morning source: backend: http_upload destinations: - id: archive backend: s3 bucket: reports prefix: weather/morning/archive - id: latest backend: s3 bucket: reports prefix: weather/morning/latest path_mapping: mode: fixed ``` Per-pipeline upload settings such as `staging_path` and `max_upload_size` should remain on the `http_upload` source. ## Authorization Semantics - Missing, malformed, or unknown bearer tokens should return `401 Unauthorized`. - Valid tokens that are not allowed for the requested pipeline should return `403 Forbidden`. - Requested pipeline ids must name configured pipelines whose source backend is `http_upload`. - Multiple upload tokens may authorize the same pipeline. - One upload token may authorize multiple pipelines. - Token values must continue to resolve through the process environment or `secrets.directory`, not YAML literal values. Idempotency records should be scoped by token id, pipeline id, and idempotency key. This avoids collisions when multiple authorized producers submit to the same pipeline. ## Producer Package Changes Add `PipelineID` to producer upload options: ```go result, err := client.UploadFiles(ctx, upload.UploadFilesOptions{ PipelineID: "weather.morning", ID: "weather.morning.brentwood", IdempotencyKey: "weather.morning.brentwood.20260607T050000Z", Files: []bundle.BundleFile{ {SourcePath: reportPath, Path: "report.md"}, {SourcePath: dataPath, Path: "data.json"}, }, }) ``` `pkg/upload` should derive `/v1/pipelines/{pipeline_id}/upload` when `PipelineID` is set. Status lookup can continue to use run ids returned by the server. The producer contract should remain: - token identifies and authenticates the producer/client; - `PipelineID` selects the configured distributor workflow; - source manifest `id` identifies the logical artifact within that workflow; - idempotency key identifies one producer run and retry group. ## Compatibility Plan Prefer a transition period: - Keep current `POST /upload` behavior for legacy configs where one token maps to exactly one `http_upload` pipeline. - Reject legacy `/upload` routing when a token is authorized for multiple pipelines, because routing would be ambiguous. - Keep rejecting `pipeline` and `pipeline_id` query parameters. - Document `/v1/pipelines/{pipeline_id}/upload` as the preferred endpoint for new clients. After the transition period, consider deprecating or removing legacy `/upload` if the compatibility burden is no longer useful. ## Implementation Work - Add `upload_tokens` config structs, defaults, validation, and secret resolution. - Update upload token resolution to produce token identities and pipeline allowlists instead of a token-to-single-pipeline map. - Add the `/v1/pipelines/{pipeline_id}/upload` HTTP route and pipeline id path validation. - Preserve `/healthz` and `/runs/` behavior. - Pass token identity into upload admission so idempotency can be scoped by token id, pipeline id, and key. - Add `PipelineID` to `pkg/upload` upload option structs and endpoint construction. - Update configuration, operation, integration, consumer, and troubleshooting docs for implemented behavior. ## Tests Important tests: - Config loading and validation for `upload_tokens`. - Startup failure for missing, empty, duplicated, or invalid upload token records. - `401` for missing or unknown bearer token. - `403` for valid token not allowed for requested pipeline. - Successful upload to two different pipelines with one token. - Successful upload to one pipeline from two different authorized tokens. - Idempotency isolation across token ids and pipeline ids. - Legacy `/upload` compatibility for one-token-one-pipeline routing. - Legacy `/upload` rejection when routing is ambiguous. - `pkg/upload` endpoint construction with `PipelineID`. - Producer package tests for missing or invalid `PipelineID`. ## Boundaries - Do not add destination selection to producer manifests. - Do not let producers specify destination ids, transforms, links, publish policy, or transfer policy through the upload API. - Do not add durable upload status or durable idempotency as part of this work; those remain separate roadmap items. - Do not add in-app public exposure policy, TLS, or rate limiting as part of this work; those remain deployment-layer concerns unless a future implementation changes that boundary.