464 lines
18 KiB
Markdown
464 lines
18 KiB
Markdown
# Post-Local-MVP Implementation Roadmap
|
|
|
|
This is the canonical active roadmap for `distributor` after the local MVP checkpoint.
|
|
|
|
The original MVP stages 1-8 are complete and are no longer listed as pending implementation work. Current behavior is documented outside the roadmap in `README.md`, `docs/cli.md`, `docs/config.md`, `docs/operations.md`, `docs/troubleshooting.md`, `docs/internal/`, `docs/integrations/markdown.md`, and `docs/policy/development.md`.
|
|
|
|
Future, planned, or aspirational behavior should remain under `docs/roadmap/` until implemented.
|
|
|
|
## Current Baseline
|
|
|
|
The implemented local MVP includes:
|
|
|
|
- standard-library CLI commands for `version`, `run`, `validate`, and `inspect`;
|
|
- YAML config loading, defaults, known-field rejection, and validation;
|
|
- accepted config shapes for `local`, `ssh`, and `s3`, with executable backend support currently limited to `local`;
|
|
- backend-rooted storage interface with typed errors, safe logical paths, traversal, `HasAny`, managed deletion, local backend, and fake backend;
|
|
- source bundle discovery, manifest parsing, RFC3339 timestamp handling, duplicate path checks, path safety checks, symlink rejection, per-file digest validation, and bundle digest validation;
|
|
- destination `.distributor.json` state parsing, validation, output metadata, and source comparison;
|
|
- local publication of source files, Markdown sidecar HTML, or both;
|
|
- destination output collision detection before writes;
|
|
- managed replacement for older destination state;
|
|
- unmanaged destination and conflict failures by default;
|
|
- deterministic dry-run output and final run summaries;
|
|
- deterministic sequential fan-out with aggregated failures;
|
|
- cleanup of outputs written during failed local publish attempts where practical;
|
|
- no-op notification hook after successful publish or replacement;
|
|
- current user, operator, internal, integration, and development documentation for implemented behavior.
|
|
|
|
The local MVP intentionally does not include executable SSH/SFTP backends, executable S3-compatible backends, force overwrite behavior, external notification adapters, warning-only digest mismatch behavior, or broad recursive destination deletion.
|
|
|
|
## Active Roadmap Stages
|
|
|
|
Implement each stage independently. Unless a stage explicitly says otherwise:
|
|
|
|
1. read `docs/policy/architecture.md`, `docs/policy/documentation.md`, `docs/policy/development.md`, and this roadmap before editing;
|
|
2. preserve current local MVP behavior;
|
|
3. keep user-facing docs limited to implemented behavior;
|
|
4. add or update focused tests for the behavior changed;
|
|
5. run the relevant package tests and `go test ./...` for cross-package changes;
|
|
6. avoid implementing later stages early.
|
|
|
|
## Stage 1: SSH/SFTP Backend
|
|
|
|
### Goal
|
|
|
|
Implement native SSH/SFTP storage backend support for sources and destinations through the existing storage interface and app-level backend factory.
|
|
|
|
### Implementation Scope
|
|
|
|
Add an SSH/SFTP adapter package under `internal/adapters/ssh`.
|
|
|
|
The backend must implement the current `internal/storage.Backend` contract:
|
|
|
|
- `ReadFile` and `OpenReader`;
|
|
- `WriteFile` and `WriteFrom`;
|
|
- `Stat`;
|
|
- `Walk`;
|
|
- `HasAny`;
|
|
- `DeleteManagedBundle`.
|
|
|
|
Use native SFTP operations rather than shelling out to `ssh`, `scp`, or `rsync`.
|
|
|
|
Authentication behavior:
|
|
|
|
- prefer SSH agent by default;
|
|
- use `known_hosts` validation by default where practical;
|
|
- support optional key-file configuration only if it can be added cleanly;
|
|
- do not support passwords in YAML in this stage.
|
|
|
|
Config execution behavior:
|
|
|
|
- use the existing accepted config shape:
|
|
|
|
```yaml
|
|
backend: ssh
|
|
uri: ssh://user@example.com:22
|
|
path: /remote/root
|
|
```
|
|
|
|
- keep secrets out of config files;
|
|
- keep config loading and validation centralized in `internal/config`;
|
|
- wire runtime construction through app-level backend factory and storage registry patterns.
|
|
|
|
Supported pipeline combinations:
|
|
|
|
- local source to SSH destination;
|
|
- SSH source to local destination;
|
|
- SSH source to SSH destination where feasible through streaming or backend-owned staging.
|
|
|
|
Safety requirements:
|
|
|
|
- enforce the same backend-rooted logical path rules as local storage;
|
|
- reject path traversal, absolute logical paths, dot segments, and backslashes;
|
|
- report or reject symlinks according to storage and bundle validation rules;
|
|
- keep deletion limited to managed output paths and `.distributor.json`;
|
|
- never delete a configured backend root;
|
|
- preserve conservative non-force conflict behavior.
|
|
|
|
### Documentation Updates
|
|
|
|
After implementation, update only current-behavior docs:
|
|
|
|
- `docs/config.md`: mark SSH as executable and document any implemented SSH-only fields.
|
|
- `docs/operations.md`: add SSH source/destination operating notes and recovery boundaries.
|
|
- `docs/troubleshooting.md`: add common SSH authentication, known-hosts, and remote path failures.
|
|
- `docs/internal/storage.md`: add implemented SSH adapter behavior and tests.
|
|
- `docs/policy/development.md`: update backend addition guidance if implementation changes the pattern.
|
|
- `examples/`: add only runnable or clearly environment-gated SSH examples.
|
|
|
|
Do not document S3 or force overwrite as implemented in this stage.
|
|
|
|
### Tests
|
|
|
|
Add unit tests for:
|
|
|
|
- SSH config execution wiring;
|
|
- URI and path handling;
|
|
- logical path validation;
|
|
- storage error translation where practical;
|
|
- `Walk` and `HasAny` behavior through test doubles or controlled fixtures;
|
|
- managed deletion boundaries;
|
|
- app-level local-to-SSH and SSH-to-local planning or wiring using fakes/mocks where possible.
|
|
|
|
Add integration tests only if they are skipped unless explicit SSH test endpoint environment variables are configured. Normal `go test ./...` must not require a live SSH server.
|
|
|
|
### Completion Criteria
|
|
|
|
- SSH/SFTP backend compiles and satisfies `storage.Backend`.
|
|
- Runtime `run` can execute supported SSH source and destination flows.
|
|
- Local MVP tests still pass.
|
|
- Normal test runs do not require a live SSH server.
|
|
- User docs accurately describe implemented SSH behavior and boundaries.
|
|
|
|
## Stage 2: S3-Compatible Backend
|
|
|
|
### Goal
|
|
|
|
Implement S3-compatible object storage backend support for sources and destinations through the existing storage interface and app-level backend factory.
|
|
|
|
### Implementation Scope
|
|
|
|
Add an S3-compatible adapter package under `internal/adapters/s3`.
|
|
|
|
The backend must implement the current `internal/storage.Backend` contract:
|
|
|
|
- `ReadFile` and `OpenReader`;
|
|
- `WriteFile` and `WriteFrom`;
|
|
- `Stat`;
|
|
- `Walk`;
|
|
- `HasAny`;
|
|
- `DeleteManagedBundle`.
|
|
|
|
Use the existing accepted config shape:
|
|
|
|
```yaml
|
|
backend: s3
|
|
endpoint: https://s3.example.com
|
|
bucket: reports
|
|
prefix: some/prefix
|
|
region: us-east-1
|
|
force_path_style: true
|
|
credentials:
|
|
access_key_id_env: DISTRIBUTOR_S3_ACCESS_KEY_ID
|
|
secret_access_key_env: DISTRIBUTOR_S3_SECRET_ACCESS_KEY
|
|
```
|
|
|
|
Credential behavior:
|
|
|
|
- read access key and secret key from the named environment variables when configured;
|
|
- support standard SDK credential discovery only if it does not weaken explicit config behavior;
|
|
- do not put literal secrets in YAML.
|
|
|
|
Object semantics:
|
|
|
|
- treat prefixes as object trees, not real directories;
|
|
- normalize configured prefix plus logical path with exact path-boundary matching;
|
|
- `Stat` must not synthesize directory metadata only because objects exist below a prefix;
|
|
- `Walk` should use object-list pagination and should not load an entire prefix into memory;
|
|
- `HasAny` should stop after the first matching object;
|
|
- `DeleteManagedBundle` must delete only listed managed output objects plus `.distributor.json`.
|
|
|
|
Write behavior:
|
|
|
|
- treat successful object PUT as publish-on-success;
|
|
- set content type from `storage.WriteOptions` where available;
|
|
- spool or buffer `WriteFrom` only when required by the SDK or backend;
|
|
- preserve overwrite checks and conservative conflict behavior.
|
|
|
|
Content type behavior should cover at least:
|
|
|
|
- `.md`: `text/markdown; charset=utf-8`;
|
|
- `.html`: `text/html; charset=utf-8`;
|
|
- `.json`: `application/json`;
|
|
- `.txt`: `text/plain; charset=utf-8`.
|
|
|
|
Supported pipeline combinations:
|
|
|
|
- local source to S3 destination;
|
|
- S3 source to local destination;
|
|
- S3 source to S3 destination where feasible through streaming or backend-owned staging.
|
|
|
|
### Documentation Updates
|
|
|
|
After implementation, update only current-behavior docs:
|
|
|
|
- `docs/config.md`: mark S3 as executable and document implemented credential behavior.
|
|
- `docs/operations.md`: add S3 source/destination layout, prefix, and recovery notes.
|
|
- `docs/troubleshooting.md`: add common S3 credential, bucket, endpoint, prefix, and permission failures.
|
|
- `docs/internal/storage.md`: add implemented S3 adapter behavior and tests.
|
|
- `docs/policy/development.md`: update backend addition guidance if implementation changes the pattern.
|
|
- `examples/`: add only safe S3 examples that use placeholder endpoints and environment variable names.
|
|
|
|
Do not document force overwrite or notification adapters as implemented in this stage.
|
|
|
|
### Tests
|
|
|
|
Add unit tests for:
|
|
|
|
- S3 config execution wiring;
|
|
- credential environment variable handling;
|
|
- key and prefix normalization;
|
|
- exact prefix boundary behavior;
|
|
- path traversal rejection;
|
|
- content type selection;
|
|
- paginated `Walk` behavior through mocks/fakes;
|
|
- early-stop `HasAny`;
|
|
- managed deletion boundaries;
|
|
- publish planning with S3 destination state fixtures.
|
|
|
|
Add integration tests only if they are skipped unless explicit S3-compatible endpoint credentials are configured. Normal `go test ./...` must not require live S3 credentials.
|
|
|
|
### Completion Criteria
|
|
|
|
- S3-compatible backend compiles and satisfies `storage.Backend`.
|
|
- Runtime `run` can execute supported S3 source and destination flows.
|
|
- Local and SSH behavior, if implemented, remain unchanged.
|
|
- Normal test runs do not require live S3.
|
|
- User docs accurately describe implemented S3 behavior and boundaries.
|
|
|
|
## Stage 3: Cross-Backend Hardening and Documentation
|
|
|
|
### Goal
|
|
|
|
Harden behavior across implemented backend combinations, improve operator-facing failures, and synchronize current-behavior documentation and examples after remote backend support exists.
|
|
|
|
### Implementation Scope
|
|
|
|
Exercise and harden representative flows across all implemented backend types:
|
|
|
|
- local source to local archive destination;
|
|
- local source to local HTML destination;
|
|
- local source to multiple destinations with different publish policies;
|
|
- local source to SSH destination, when SSH is implemented and test credentials exist;
|
|
- SSH source to local destination, when SSH is implemented and test credentials exist;
|
|
- local source to S3 destination, when S3 is implemented and test credentials exist;
|
|
- S3 source to local destination, when S3 is implemented and test credentials exist.
|
|
|
|
Improve error context where practical for:
|
|
|
|
- invalid config;
|
|
- invalid source manifest;
|
|
- digest mismatch;
|
|
- destination conflict;
|
|
- unmanaged destination path;
|
|
- backend read, write, stat, walk, and delete failures;
|
|
- transform failures;
|
|
- partial fan-out failures.
|
|
|
|
Ensure errors and logs identify pipeline id, destination id, bundle path or id, backend type, and logical path where useful without exposing secrets.
|
|
|
|
Do not add force overwrite behavior in this stage.
|
|
|
|
### Documentation Updates
|
|
|
|
Update current-behavior docs after hardening:
|
|
|
|
- `README.md`: keep the quickstart local unless remote examples become safe and concise.
|
|
- `docs/cli.md`: document any changed output or diagnostics.
|
|
- `docs/config.md`: ensure backend support status and config reference match implementation.
|
|
- `docs/operations.md`: document cross-backend state layout, retry behavior, and recovery caveats.
|
|
- `docs/troubleshooting.md`: add recurring SSH/S3 failure modes discovered during hardening.
|
|
- `docs/internal/`: update storage, publish, app, and config internals where behavior changed.
|
|
- `examples/`: keep examples copyable and free of secrets; remote examples must rely on placeholders and environment variables.
|
|
|
|
### Tests
|
|
|
|
Add or expand tests for:
|
|
|
|
- dry-run across multiple destinations and backend types;
|
|
- partial failure behavior;
|
|
- repeated run idempotency;
|
|
- older/newer destination state behavior across backends;
|
|
- destination state output metadata accuracy;
|
|
- generated HTML output metadata accuracy;
|
|
- destructive replacement safety across implemented backends;
|
|
- error context for common failures.
|
|
|
|
Integration tests for SSH or S3 must remain opt-in through environment variables.
|
|
|
|
### Completion Criteria
|
|
|
|
- Implemented backend combinations behave consistently through the common pipeline path.
|
|
- Repeated runs are idempotent.
|
|
- Destructive paths remain bounded to managed destination bundle paths.
|
|
- Operator-facing errors are actionable.
|
|
- Current-behavior docs and examples match implemented backend support.
|
|
|
|
## Stage 4: Explicit Force Overwrite
|
|
|
|
### Goal
|
|
|
|
Introduce explicit operator-requested force behavior for controlled overwrite cases that remain intentionally unsupported by default.
|
|
|
|
### Implementation Scope
|
|
|
|
Add a CLI-only force option:
|
|
|
|
```bash
|
|
distributor run --config config.yml --force
|
|
```
|
|
|
|
Force must be explicit per run. Do not add a persistent config default for force behavior.
|
|
|
|
Define and implement force planning for:
|
|
|
|
- unmanaged non-empty destination paths;
|
|
- destination state with a different source id;
|
|
- destination state with matching source id and matching `created` timestamp but different digest;
|
|
- destination state with mismatched `pipeline_id` or `destination_id`;
|
|
- destination newer than source when transfer policy explicitly allows replacement.
|
|
|
|
Once force behavior exists, update transfer policy validation only for values supported by implemented force behavior:
|
|
|
|
- `on_destination_newer: replace`;
|
|
- `on_conflict: replace`.
|
|
|
|
Safety requirements:
|
|
|
|
- non-force behavior remains unchanged and conservative;
|
|
- dry-run must show destructive force actions before any forced run;
|
|
- force must never delete above the resolved destination bundle path or configured destination prefix;
|
|
- local replacement should remain staged where practical;
|
|
- S3 replacement must remain constrained to the destination bundle prefix;
|
|
- managed state should still be written only after successful output writes;
|
|
- logs and output must clearly mark force decisions.
|
|
|
|
### Documentation Updates
|
|
|
|
After implementation, update:
|
|
|
|
- `docs/cli.md`: document `--force` syntax and dry-run workflow.
|
|
- `docs/config.md`: document newly accepted transfer policy values and note force is CLI-only.
|
|
- `docs/operations.md`: document safe force workflow and recovery boundaries.
|
|
- `docs/troubleshooting.md`: describe when force may be appropriate and when it remains unsafe.
|
|
- `docs/internal/publish.md` and `docs/internal/state.md`: document force planning and comparison handling.
|
|
|
|
Do not document force as a default or config-only behavior.
|
|
|
|
### Tests
|
|
|
|
Add tests for:
|
|
|
|
- force rejected or unavailable when the flag is absent;
|
|
- unmanaged non-empty destination overwritten only with force;
|
|
- different source id overwritten only with force and allowed policy;
|
|
- same id and created timestamp with different digest overwritten only with force and allowed policy;
|
|
- destination newer replaced only with force and allowed policy;
|
|
- pipeline or destination id mismatch overwritten only with force and allowed policy;
|
|
- dry-run reports destructive force actions without writing;
|
|
- force deletes only bounded destination bundle paths;
|
|
- local, SSH, and S3 backends, where implemented, preserve deletion boundaries.
|
|
|
|
### Completion Criteria
|
|
|
|
- Force overwrite behavior is explicit, logged, dry-runnable, and test-covered.
|
|
- Default non-force behavior remains unchanged.
|
|
- User docs clearly describe force risks and safe workflow.
|
|
|
|
## Stage 5: Release Readiness
|
|
|
|
### Goal
|
|
|
|
Perform a final quality pass before treating `distributor` as ready for routine use against real producer pipelines and implemented destination backends.
|
|
|
|
### Implementation Scope
|
|
|
|
Review:
|
|
|
|
- package boundaries against `docs/policy/architecture.md`;
|
|
- contributor workflow against `docs/policy/development.md`;
|
|
- user docs against `docs/policy/documentation.md`;
|
|
- CLI UX and command output;
|
|
- config validation and examples;
|
|
- manifest and state compatibility;
|
|
- destructive operation safety;
|
|
- backend error handling;
|
|
- logging and diagnostics for unattended operation;
|
|
- test coverage for core invariants.
|
|
|
|
Do not add new product features in this stage.
|
|
|
|
### Documentation Updates
|
|
|
|
Update current-behavior docs only for issues found during the readiness review.
|
|
|
|
If release packaging, version injection, or installation workflow is added, document it in the appropriate current-behavior user or development docs.
|
|
|
|
### Tests
|
|
|
|
Run:
|
|
|
|
```bash
|
|
go test ./...
|
|
```
|
|
|
|
Also verify representative CLI examples that are documented as runnable.
|
|
|
|
### Completion Criteria
|
|
|
|
- A dry-run can be performed safely against real configured sources and destinations.
|
|
- Repeated runs are idempotent.
|
|
- Destructive replacement cannot occur outside managed destination bundle paths.
|
|
- Current docs accurately reflect the application.
|
|
- The project is ready to deploy against one real producer pipeline.
|
|
|
|
## Deferred Work
|
|
|
|
The following work remains intentionally deferred unless a future roadmap promotes it:
|
|
|
|
- external notification adapters such as email, ntfy, Gotify, or Pushover;
|
|
- RSS or Atom feed generation;
|
|
- static site index pages beyond sidecar HTML output;
|
|
- destination path remapping rules;
|
|
- HTML themes beyond the minimal deterministic template;
|
|
- full plugin architecture;
|
|
- web UI;
|
|
- report editing;
|
|
- producer pipeline execution;
|
|
- database-backed state;
|
|
- complex retry queues;
|
|
- concurrent publication workers;
|
|
- symlink support;
|
|
- warning-only digest mismatch handling;
|
|
- password-based SSH authentication in YAML;
|
|
- broad recursive or prefix deletion outside explicitly bounded force behavior.
|
|
|
|
## Validation
|
|
|
|
For roadmap-only edits:
|
|
|
|
```bash
|
|
git status --short
|
|
git diff -- docs/roadmap
|
|
rg -n "docs/roadmap/(packages|contracts|storage|config|documentation)\\.md" README.md docs examples
|
|
rg -n "docs/roadmap/(packages|contracts|storage|config|documentation)\\.md" .
|
|
rg -n "SSH|S3|--force|force overwrite|notification adapter|future|planned" README.md docs/*.md docs/internal docs/policy examples
|
|
```
|
|
|
|
Also search `docs/roadmap` for old MVP stage headings and titles from deleted roadmap files. That check should return no matches.
|
|
|
|
The final SSH/S3/force/future-work search is not expected to return zero results. Review matches and confirm they are either under roadmap material or clearly marked as unsupported current behavior.
|
|
|
|
Go tests are not required for documentation-only roadmap rationalization unless examples, behavior docs, or code change.
|