|
| 1 | +--- |
| 2 | +'@objectstack/lint': minor |
| 3 | +'@objectstack/metadata-protocol': minor |
| 4 | +--- |
| 5 | + |
| 6 | +The runtime metadata publish gate refuses an `api` flow with no per-flow secret, and reads a secret the flow read path withheld as present (#20611). |
| 7 | + |
| 8 | +Clause-②: yes (narrowing) |
| 9 | + |
| 10 | +<!-- adr-0087: not-required (no-migration-prescription) Nothing authorable changes spelling or type: `packages/spec` is untouched, and the start node `config` stays the open record it was. What changes is that the runtime metadata write door now refuses one authored shape at publish: an `api`-bound flow whose start node carries no usable `config.secret`. `objectstack migrate meta` could not rewrite that shape even in principle, because the missing value is a shared secret only the author and the sending system can supply. Rows at rest are not judged or rewritten; the automation engine has refused to register such a flow since 17.5.0, and that load path's disposition is recorded in its own published changelog entry. --> |
| 11 | + |
| 12 | +**BREAKING** — an accept-set narrowing on the runtime metadata write door, |
| 13 | +shipped as `minor` under the launch-window convention (`check-changeset-no-major` |
| 14 | +refuses `major` until GA; breaking-ness is carried by this banner and the ADR-0087 |
| 15 | +disposition above, not by the level). An `active` save through `/meta` of an |
| 16 | +`api`-bound flow whose start node carries no usable `config.secret` (a |
| 17 | +`PUT /api/v1/meta/flow/:name`, or the publish of such a draft) used to be stored; |
| 18 | +the automation engine then refused to register it (`400` on the `/automation` |
| 19 | +doors, a skip with a warning at boot). It is now refused at the save with |
| 20 | +`422 INVALID_METADATA`, the issue naming `flow-api-trigger-secret-missing` at the |
| 21 | +start node's `config.secret`, and nothing is stored. A draft save is still |
| 22 | +accepted; its publish is refused the same way. |
| 23 | +**One-line fix:** set a non-blank `config.secret` on the flow's start node — or, |
| 24 | +for a flow that is only ever started explicitly, declare `type: 'autolaunched'` |
| 25 | +with no `triggerType: 'api'`. |
| 26 | + |
| 27 | +**What does not change: a signed flow's round trip.** Every served flow definition withholds the start node's `config.secret`, so a body saved back after a read arrives without it, and the save restores the stored secret only after every gate has run, so that no gate handles a restored credential. The gate is now told WHERE the save will restore a credential from the stored row: those positions only, never the values. `flow-api-trigger-secret-missing` reads a secret that was withheld and is stored as present, and one that is absent and not stored as missing. So a GET → edit → PUT of a signed flow, and the first save of a code-authored flow whose secret is in the app's source, keep passing and keep their secret. An explicit empty `config.secret` is the author's own value and is refused as blank. |
| 28 | + |
| 29 | +`@objectstack/lint`: |
| 30 | + |
| 31 | +- `validateFlowApiTriggerSecret` now runs on the runtime publish gate too (`surfaces` `['cli', 'runtime-publish']`, `runtimeTypes: ['flow']`). Its `surfaceReason` is gone. |
| 32 | +- `AuthoringRuleContext` gains an optional `restoredCredentialPaths`: a `ReadonlySet<string>` of stack-relative positions in the rules' own finding-path spelling (`flows[0].nodes[1].config.secret`). Only the runtime publish gate sets it; `runAuthoringRules` never forwards it, so `os validate`, `os build` and `os lint` judge the author's own values as before. |
| 33 | +- `runRuntimeAuthoringRules` accepts an optional `restoredCredentialPaths`: item-relative dotted positions in the `@objectstack/spec/kernel` redactor registry's `redactedKeys` spelling (`nodes.1.config.secret`). The gate re-spells them against the written item's place in its snapshot. |
| 34 | +- `validateFlowApiTriggerSecret(stack, options?)` accepts an optional `{ restoredCredentialPaths }`, and treats a listed start-node secret position as present. |
| 35 | + |
| 36 | +`@objectstack/metadata-protocol`: `saveMetaItem` hands the runtime authoring gate the positions its own credential carry-forward will fill, computed from the same stored body. This costs one indexed `sys_metadata` read on an `active` save of a type with a registered redactor (`datasource`, and `flow` where the automation plugin registers one), and nothing for any other type or for a draft save. The carry-forward itself is unchanged and still runs after every gate. The draft→active promotion judges the stored draft row, which already holds what that draft's save carried forward, so it needs no such positions. |
0 commit comments