From 9185c145a1e4aa0db07910800c1559a680244147 Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 2 Jun 2026 08:00:44 +0000 Subject: [PATCH] docs(prd): add pipelock yaml contract --- .../0037-pipelock-yaml-render-contract.md | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 docs/prds/0037-pipelock-yaml-render-contract.md diff --git a/docs/prds/0037-pipelock-yaml-render-contract.md b/docs/prds/0037-pipelock-yaml-render-contract.md new file mode 100644 index 0000000..3406f72 --- /dev/null +++ b/docs/prds/0037-pipelock-yaml-render-contract.md @@ -0,0 +1,106 @@ +# PRD 0037: Pipelock YAML Render Contract + +- **Status:** Draft +- **Author:** didericis-codex +- **Created:** 2026-06-02 +- **Issue:** #130 + +## Summary + +Lock down the contract between `pipelock_build_config` and +`pipelock_render_yaml` so hand-rendered pipelock YAML stays aligned with the +structured config bot-bottle builds. Keep the stdlib-only renderer, but add +shape validation and semantic tests for every supported section. + +## Problem + +`bot_bottle/pipelock.py` builds a structured dict and then renders a fixed YAML +shape by hand. This avoids a runtime YAML dependency, but it also means the +renderer directly indexes expected keys. If `pipelock_build_config` adds, +renames, or conditionalizes a section, rendering can fail at runtime or emit +YAML that no longer matches the config semantics. + +Existing tests assert important rendered fragments, but they do not fully lock +the build/render contract or optional-section combinations. A mismatch here can +weaken DLP enforcement or break bottle launch after a future pipelock policy +change. + +## Goals / Success Criteria + +- Keep the renderer stdlib-only. +- Define the supported pipelock config shape in one place. +- Fail clearly when `pipelock_render_yaml` receives an unsupported or malformed + config shape. +- Add tests covering all supported sections: + - base allowlist and forward proxy. + - seed phrase detection toggle. + - DLP and request-body/header scanning. + - TLS interception and passthrough domains. + - SSRF IP allowlist. +- Add semantic tests that compare structured config values to rendered YAML + output without relying only on brittle substring assertions. +- Preserve current rendered YAML for existing configs unless a clearer failure + path requires an error message change. + +## Non-goals + +- No PyYAML or other runtime dependency. +- No change to pipelock policy defaults. +- No change to egress-to-pipelock topology. +- No change to pipelock image version or config schema beyond validation of the + shape bot-bottle already emits. + +## Scope + +In scope: + +- `bot_bottle/pipelock.py` render helpers and validation. +- Unit tests in `tests/unit/test_pipelock_yaml.py` and related focused + pipelock tests. +- Small helper functions for typed access to config sections, if useful. + +Out of scope: + +- Launch/backend changes. +- Integration tests that start a real pipelock container. +- Changing the manifest schema for route-level pipelock policy. + +## Design + +Treat `pipelock_render_yaml` as a serializer for the narrow config shape +produced by `pipelock_build_config`, not as a generic YAML renderer. Before +rendering a section, validate that required keys exist with the expected +primitive/list/dict types. Missing or unsupported shapes should raise a clear +`ValueError` naming the section and key. + +Tests should cover both normal output and failure cases. Because the project is +stdlib-only, semantic tests can use a small purpose-built parser for the exact +rendered shape or compare rendered lines to values from the structured config +through helper assertions. The goal is to detect drift between config dict and +YAML without adding a general YAML dependency. + +Optional sections should be exercised in combinations: + +- no TLS and no SSRF. +- TLS enabled with empty and non-empty passthrough domains. +- SSRF enabled with one or more IP/CIDR entries. +- all optional sections enabled together. + +## Testing Strategy + +- Extend `tests/unit/test_pipelock_yaml.py` with semantic assertions tying each + rendered section back to the config dict. +- Add malformed-config tests for missing required keys and wrong section types. +- Keep existing render fragment tests where they protect exact pipelock syntax. + +Run: + +- `python3 -m unittest tests.unit.test_pipelock_yaml` +- `python3 -m unittest tests.unit.test_pipelock_allowlist` +- `python3 -m unittest discover -s tests/unit` + +## Open Questions + +- Should malformed config errors be `ValueError`, matching current + `pipelock_build_config` validation, or a new internal exception type? Prefer + `ValueError` unless a caller needs to distinguish serializer errors.