faucet_cli/templates/mod.rs
1//! Pipeline template registry (#444) — register a parameterized config **once**,
2//! then trigger runs by `{id, params}`.
3//!
4//! This module is the single implementation the three surfaces adapt to:
5//!
6//! | Surface | Register | List / get | Trigger |
7//! |---|---|---|---|
8//! | HTTP (`faucet serve`) | `POST /v1/templates` | `GET /v1/templates[/{id}]` | `POST /v1/templates/{id}/runs` |
9//! | MCP (`--mcp` / `faucet mcp`) | `register_template` | `list_templates` / `get_template` | `run_template` |
10//! | CLI | `faucet template register` | `faucet template list` / `show` | `faucet template run` |
11//!
12//! [`register`] appends a version; [`launch`] makes one live; [`rollback`] returns
13//! to the one before it; [`promote`] moves an environment channel;
14//! [`set_deprecated`] retires a template; [`resolve_version`] turns a selector into
15//! a concrete version; [`materialize`] turns `{id, version, params, env}` back into
16//! a ready-to-run config document.
17//!
18//! ## Builds, releases, and lifecycle
19//!
20//! Versions are **numeric and auto-incrementing** — every register appends
21//! `max + 1` and is otherwise **inert**. Registering a nightly or a feature build
22//! moves nobody; only an explicit [`launch`] does. That separation is the point of
23//! the model: unpinned callers ride the *blessed* release, not the most recent
24//! upload.
25//!
26//! Over the numbers sits a **closed set of channels**
27//! ([`VersionChannel`](crate::serve::history::templates::VersionChannel)) —
28//! three derived (`stable` = the launched version and the default, `previous` =
29//! the one launched before it, `newest` = the highest number) plus the assignable
30//! environments `dev`, `test`, `staging`, `pre-prod`, `canary`, `prod`. The set is
31//! closed on purpose: an open tag namespace becomes a second, unreviewable naming
32//! system in which `prd` silently creates a channel nobody watches. `latest` is
33//! rejected outright as ambiguous between `stable` and `newest`.
34//!
35//! A **template** (not a version) carries the lifecycle status
36//! ([`TemplateStatus`](crate::serve::history::templates::TemplateStatus)):
37//! `draft` until something is launched, then `launched`, or `deprecated` once
38//! retired. It is derived from the launch log plus a deprecation marker, so it can
39//! never disagree with the registry's contents. Everything downstream of `materialize` is the ordinary run
40//! path (`load_submission` → `expand` → `run_expanded`), so templates inherit
41//! every existing guarantee — matrix/topology expansion, the exactly-once gate,
42//! idempotency keys, RBAC, and the audit log — for free.
43//!
44//! ## What is and isn't persisted
45//!
46//! The body is stored **verbatim**: `${env:…}` / `${vault:…}` / `${secret:…}`
47//! directives stay unresolved and are resolved at trigger time against the
48//! *executing server's* environment and credentials — exactly the privilege
49//! surface of a normally-submitted config. Caller-supplied `secret: true` param
50//! values are never persisted at all; see [`materialize`] for the one place that
51//! distinction is enforced.
52
53pub mod store;
54
55pub use store::{
56 LaunchOutcome, Materialize, MaterializedConfig, RegisterRequest, TemplateStore, launch,
57 list_with_state, materialize, promote, register, resolve_store_url, resolve_version, rollback,
58 set_deprecated, template_state,
59};