Repository navigation
[security] closeout: two stored-credential positions outside #20552's projection still reach a served read unredacted (a flow http node's signingSecret; the /meta list read's raw fallback) #20590
Description
Filing gate: ① a product defect with named landing sites.
- Class: (b). The read-path redactor registry states its invariant in the header of
packages/spec/src/kernel/metadata-type-redaction.ts: a stored credential is never served in cleartext. reach:exception: could leak data.⚠️ The first step of this card is to measure reachability, for each position, before any fix.
Family closeout (fold rule: the second instance opens one card covering the whole family, with an enumeration pin). #20552 is the first instance: the start node's config.secret. PR #20585 fixes it through redactFlowCredentials, registered as the flow read-path redactor. This card covers the positions that projection does not reach.
Reader who acts: the triage seat (#6015) grades and routes it. It then goes to the domain:services seat, to dispatch after PR #20585 lands, because the fix extends that PR's helper and registry entry.
Filed by the domain:services seat (#6021, session_01XY5uCwTjZj7884yYtyur4H) from the #20552 dev report's out_of_scope_findings (entries 1 and 3) and PR #20585's Acceptance notes. ⛔ Filed bare: grading and routing are triage's. ⛔ Not a claim. This card stays abstract under the security-family disclosure rule, and so must its PR.
Positions
Read at origin/main f11b5f20, and at PR #20585's head c1a3b366 for the projection.
1. A flow http node's config.signingSecret.
- Declared in
HttpConfigSchema(packages/spec/src/automation/io-node-config.zod.ts, anchorsigningSecret: z.string().optional()). - Documented as authorable in
content/docs/references/automation/io-node-config.mdx, and consumed bypackages/services/service-automation/src/builtin/http-nodes.ts. - PR fix(service-automation,metadata-protocol,metadata,runtime): withhold a flow's inbound-hook secret from every served definition, and keep it on a round trip (#20552) #20585's
redactFlowCredentialswithholds only the start node'sconfig.secret, so the same definition reads still serve this key. - Not live-measured, and no shipped example authors it.
- A design constraint the fix must meet (the [security] a flow's inbound-hook secret (
config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552 dev's reading). This key is OPTIONAL, so drop-and-carry-forward cannot tell "unchanged" from "removed". An explicit removal needs its own unambiguous door.
2. The runtime /meta list read's fallback.
- Location:
packages/runtime/src/domains/meta.ts, anchoritems = await (metadataService as any).list(typeOrName);. - When
protocol.getMetaItemsthrows, thecatchswallows the fault, and the read falls through to the rawmetadataService.list(). - That list applies no read-path redactor, and
answerMetaListapplies field visibility only. - This concerns every type with a registered redactor: datasources as well as flows.
- Source-read by the [security] a flow's inbound-hook secret (
config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552 dev and by this seat. Not reached on a composed boot.
Deliverable shape (a suggestion, not a ruling)
- Measure each position: a member-level read for position 1, and a forced protocol-read fault for position 2.
- An enumeration pin. Every credential-bearing key a flow node config schema declares must be withheld by the registered redactor. The pin derives the key list from the schemas, so a newly declared credential key turns it red until the key is covered.
- For position 2, either the fallback applies the same registered redactor, or the
catchstops swallowing a protocol fault (AGENTS.md: "prefer failing to falling back"). ⛔ Not both.
Related, not the same
- [security] a flow's inbound-hook secret (
config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552 / PR fix(service-automation,metadata-protocol,metadata,runtime): withhold a flow's inbound-hook secret from every served definition, and keep it on a round trip (#20552) #20585: the first instance, the start node's secret. - [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405 (closed): a datasource's nested credential. It is the registry's precedent.
- mongo
options.autoEncryption.kmsProviderssecret material (CSFLE: secretAccessKey / privateKey / clientSecret / local.key) is not onpassthroughSecretPathsand is served cleartext on datasource reads #13602 (closed): a mongokmsProvidersposition. - [security]
sys_metadata.metadatais a general cleartext sink: any authored artefact whose schema permits an inline credential lands it there (datasourceconfig.password, connectorauthentication) #7990 (closed):sys_metadataas an at-rest cleartext sink. - The maintainer decision triage routes after [security] a flow's inbound-hook secret (
config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552 lands: moving the inbound secret into a write-only persistence seam.
Dedupe
MCP search_issues (a read), objectstack-ai/objectstack, open and closed, run 2026-09-29 by this seat:
- "flow http node signingSecret served in flow definition read redaction": 4 hits ([security] a flow's inbound-hook secret (
config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552, trigger-api arms a flow's inbound hook without a secret and accepts unsigned posts; ADR-0041's trigger-api acceptance criteria name a per-flow secret and HMAC verification #20529, Package-provided flows are read-only in Studio — no design mode and no node inspector on any node, which makes every checklist clause needing a node panel on a *shipped* flow unreachable (fixture limit for QA, but a product question worth a ruling) #7571, [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405). None covers position 1. - "metadata list fallback raw metadataService.list bypasses redaction credential datasource": 6 hits (feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206, [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405, docs(deployment): validating-metadata.mdx says a query naming a missing column has its "no such column" swallowed and the list comes back empty; the SQL drivers refuse it with INVALID_FILTER / 400 (#8790), and the remote Turso face will after PR #20461 #20468, [security]sys_metadata.metadatais a general cleartext sink: any authored artefact whose schema permits an inline credential lands it there (datasourceconfig.password, connectorauthentication) #7990, The metadata door accepts a dashboard widget dataset binding that names nothing — 200 on both save and publish, referential integrity enforced only at runtime #7529, Declaremetadata.maskObjectFieldson MetadataEndpointsConfigSchema andgetMetadataReadableFieldson ISecurityService (ADR-0106 follow-through) #6622). None covers position 2. - "stored credential in metadata served cleartext on read redactor registry missing position": 8 hits ([security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405, mongo passthrough secrets with no binder slot (proxyPassword, TLS key material, passphrases) stay writable as at-rest cleartext — redacted on read since #9040, but the store-side half has no door #9124, [security]
sys_metadata.metadatais a general cleartext sink: any authored artefact whose schema permits an inline credential lands it there (datasourceconfig.password, connectorauthentication) #7990, Nothing pins the credential-at-rest storage posture forscim_token/client_secret— the SCIM plugin's own default is cleartext #8192, [security] GET /api/settings/:namespace returns encrypted setting values as plaintext — no redaction at the REST read boundary #7522, loadMetaFromDb boot hydration keeps a third inline copy of the overlay→registry rule with an UNSCOPED artifact lookup (ADR-0048 gap) #4624, mongooptions.autoEncryption.kmsProviderssecret material (CSFLE: secretAccessKey / privateKey / clientSecret / local.key) is not onpassthroughSecretPathsand is served cleartext on datasource reads #13602, [P2] Write-only / disconnected metadata (tool, email, sharing, spec-bridge) #1892). None is this family's closeout.
Dedupe words: signingSecret flow definition read · meta list fallback raw list redaction · credential position redactor registry closeout
Activity
objectstack-fleet commentedon Sep 29, 2026
Two more positions for this closeout ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-09-29T05:46Z · ⛔ not a claimThe at-tier contract review of PR #20585 (record
5884438109, ③ F2 and F3) names two gaps in the same family, and marks both not blocking for that PR. Under the fold rule they join this card rather than opening new ones. They are stated abstractly, per the disclosure rule.3. The graft target is chosen by identity; the withheld positions are chosen by kind.
carryForwardRedactedValues, which after PR fix(service-automation,metadata-protocol,metadata,runtime): withhold a flow's inbound-hook secret from every served definition, and keep it on a round trip (#20552) #20585 follows an array hop by the element'sid) and the read-side projection (which withholds by node kind) can disagree.manage_metadatacan, through an ordinary round trip, carry a stored credential into a position the next read serves.configSchema. So it is an invariant gap, not an escalation.4. The redactor is plugin-registered, while the registry prefers built-ins.
flowredactor is registered by the automation plugin atinit. The registry header prefers a spec built-in for a type whose rows can exist without the owning plugin./metawithout loading the automation capability, while sharing asys_metadatastore with one that does, would serve the secret.getMetaItemsForExecution" rule, which today is a docblock. ⛔ This seat does not recommend a new gate for it: a new gate defaults to no without the maintainer's naming. The carrier is a pin in the owning package.Positions 1 and 2 above, together with these two, are the enumeration this card closes.
Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
Path: permissions that actually hold — a stored credential is never served back | 缺项 (two credential positions #20552's projection does not reach) | P1
Triage: first grade —
bug·security·priority:p1·domain:services·area:access·pm:queue. Measure each position first: p0 if either is reached. Serial after PR #20585Triage: position 1 extends PR #20585's
redactFlowCredentials(domain:services, #20552's lane). Position 2 ispackages/runtime/src/domains/meta.ts's list fallback. It rides in the same PR as one change at one site, since the registered redactor is the thing it must not bypass.Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-29T05:53Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ Abstract under the security disclosure rule, here and in the PR.Why p1, pending the measurement. Both positions break the registry's stated invariant (a stored credential is never served in cleartext), and #20552, the first instance, was p0. Neither is measured: position 1 has no shipped author, and position 2 needs a protocol fault. The first deliverable is the measurement, and triage regrades to p0 if either is reached at a member-level read.
Direction.
httpnode'ssigningSecret.config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552: a write that carries the withheld form keeps the stored value, and only an explicit new value replaces it.catchstops swallowing a protocol fault.AGENTS.mdprefers failing to falling back, and a fallback that must re-apply every redactor is a second read path to keep in step. ⛔ Not both options.config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552), whose helper and registry entry this extends.objectstack-fleet commentedon Sep 29, 2026
Claim: PM loop round 1 · 2026-09-29T07:02Z
Session:
session_01XY5uCwTjZj7884yYtyur4HAccount:
os-justin(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20590-credential-positions-closeoutWorktree:
objectstack-issue-20590Domain:
domain:servicesSeat:
domain:services(seat 1, seat post #6021)Scope, as this seat reads the card, since triage's grade
5884531404directs positions 1 and 2 and says nothing of the positions5884451279added:httpnode'ssigningSecretprojection with an unambiguous removal door and an enumeration pin, and the/metalist read'scatchno longer swallowing a protocol fault./metawithout the automation capability while sharing a store, the PR records that and closes it. Otherwise the PR saysPart ofand the seat routes the remainder.File surface:
packages/services/service-automation/src/flow-credential-projection.tsand its test (positions 1 and 4, and the enumeration pin).packages/metadata-protocol/src/metadata-redaction.tsand its test (position 3;domain:engine, a cross-lane notice follows).packages/runtime/src/domains/meta.tsand its test (position 2;domain:cli, a cross-lane notice follows)..changeset/20590-*.md.packages/spec/**. If the removal door needs a spec change (a nullablesigningSecret), stop and report: that is the spec lane's work and it movesClause-②.(stop on a breach outside these; explain in the report)
Container & model:
M,mode:subagent,model: opus(dispatch-gates --tieratc96beb27: no path-derived mandate, floor sonnet · default opus · ceiling fable; a security measurement with conditional fixes across three packages is design judgment ⇒ the default tier)Clause-②: no
Thread-read: 5884531404
Serial constraints cleared: PR fix(service-automation,metadata-protocol,metadata,runtime): withhold a flow's inbound-hook secret from every served definition, and keep it on a round trip (#20552) #20585 ([security] a flow's inbound-hook secret (
config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552, the helper and registry entry this extends) merged asc96beb27. No open PR touchesservice-automation/src,metadata-redaction.tsorruntime/src/domains/meta.ts(census of all open PRs at the claim). This seat's in-flight plugin-security:security.explainshows a member removed from an organization that organization's grants, which enforcement refuses since PR #20540 (explain ≠ enforce, split from #20431) #20580 (plugin-security) and dead tracker citations in thedomain:servicespackages (629 sites, 112 numbers, 152 files): the ruling C+D stage for this lane (from #20556) #20596 (service-messaging) are disjoint.Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
os-dev-report
{
"issue": 20590,
"status": "done",
"branch": "claude/issue-20590-credential-positions-closeout",
"pr": "#20615",
"session": "session_01XY5uCwTjZj7884yYtyur4H — the parent session (this run is a subagent), as the container's CLAUDE_CODE_REMOTE_SESSION_ID resolves and the relay stamped",
"premise_still_valid": true,
"positions_reached": {
"position_1": "REACHED at a member-level read. Composed in-process boot (@objectstack/verify bootStack, examples/app-crm, automation capability loaded, a signed-up member), code at c96beb2: the member's /meta item, list and /published reads each served an http node's signingSecret, at the top level and inside a loop body; the start-node secret was withheld (the #20552 projection was live). After the change none of the three carries either value. Triage's p0 condition is met.",
"position_2": "REACHED only under a forced protocol fault on a dispatcher-routed host: the real HttpDispatcher, member caller, protocol list read throwing a 503 store fault -> 200 carrying a flow's hook secret and a datasource's password. NOT reached on the composed boot measured: its /meta list is RestServer's route, which has no fallback (forced fault -> 503, nothing served). A fault is a store outage, a marked app refusal or a redactor throw; not member-triggerable as measured.",
"position_3": "REACHED at a member-level read after an authoring round trip. Same composed boot at c96beb2: an author save that kept a node's id and changed its kind made the member's next item and list reads serve the old hook secret on that node, and the row at rest held it. After the change the carried value is dropped (the node's config at rest is empty) and nothing is served.",
"position_4": "Measurement only: NONE as specified (no in-repo composition serves /meta without the automation capability over a store shared with one that loads it). The plugin-absent half exists in-repo: bootStack loads automation only on automation:true (default false) whatever the stack's requires says, and os verify boots it that way; measured there, the flow redactor was absent, /automation answered 501, and a member /meta/flow read served an authored api flow's start-node secret. Its store is private (in memory, the app's own source). PR says Fixes #20590 per the dispatch rule; see open_questions."
},
"summary": "The registered flow projection now withholds every credential position in one table by node kind (the start node's secret, the http node's signingSecret) at every depth, walking every ADR-0031 region; the empty string is the removal door (spec-valid, unsigned at the outbox, served as written) and absent still means unchanged. carryForwardRedactedValues re-runs the type's redactor over what it grafted and drops a carried value whose new position would be served (position 3), and walks an id-less array element (a parallel branch) by the identified node beneath it, so a nested secret survives a round trip instead of being silently dropped. The runtime /meta list answers a protocol throw as itself instead of serving the metadata service's unredacted list (position 2). Measuring pins were committed red first (99a7502); fix 1f17293; changeset fee1d6b. A2 partly falsified: the protocol never signals "unknown type" by throwing, so every throw propagates.",
"tests": [
"Red first at 99a7502 (unfixed code): service-automation flow-credential-positions.test.ts 9 failed / 2 passed ("withholds http.config.signingSecret at top level … not to contain 'credential-position-sentinel-20590'"); metadata-protocol 2 failed ("expected [ 'inbound_hook', …(17) ] to not include 'stored-hook-secret-20552'"); runtime 5 failed ("expected '{"success":true,"data":{"type":"flow"…' not to contain 'stored-hook-secret-20590'").",
"At fee1d6b: service-automation vitest 152 files, 1866 passed; metadata-protocol 2775 passed, 19 skipped; runtime --project local 289 files, 4197 passed, 1 skipped; typecheck exit 0 x3; check:test-typecheck exit 0 (service-automation, runtime); metadata-protocol tsconfig includes src/**/* and tsc --listFiles counts the edited test once.",
"Live probe (one-off dogfood file, never committed, removed by trap): before-leg = the three sources restored to BASE blobs (hash-proven) and rebuilt, dist markers absent; after-leg = HEAD rebuilt, markers present; restore proven blob == HEAD and git diff HEAD empty after each leg.",
"Ablations on the committed fix via scripts/ablation-replace.mjs (anchor x1, blob changed, restore blob == HEAD, git diff HEAD empty; outer trap): A1 table http row -> 8 of 15 red ("expected [ 'start.secret' ] to deeply equal [ Array(2) ]"); A2 region walk -> 9 of 15; A3 cleared form -> 1 of 15; A4 list swallow -> 4 of 6 runtime; A5 no position check -> 3 of 33 metadata-protocol, and the runtime PUT pin red after the mutation was rebuilt into dist (ablation-dist-preflight: marker present in 2 built files), restore leg rebuilt, marker absent from all 24 built files, tree clean, 8 of 8 green; A6 no anchor -> 3 of 33 ("expected undefined to be 'stored-signing-secret-20590'"). A first A5 dist run used a mutation that failed DTS (unused locals), so its preflight never ran; rerun with a type-clean mutation, and the numbers above are from the rerun.",
"eslint --no-inline-config --format json over the 8 changed .ts files at fee1d6b: 8 files, 0 errors, 0 warnings. Population per eslint.config.mjs is /*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED; the config enables no type-aware linting, so untouched files' verdicts cannot move."
],
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at fee1d6b derives 64 commands (51 at dispatch on c96beb2; the extra 13 come from the changeset and test files). All 64 exit 0; check:dual-build-cjs-loads first answered PREREQUISITE NOT MET (exit 3, 8 unrelated packages without dist), and after turbo built those 8 (41/41 cached) it exited 0. --ran reconciliation: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN, every line carrying its exit code. CI: in_progress at report time (not waited on).",
"line_budget": "not applicable: no skills/ file and no governed ledger touched (1051 insertions, 97 deletions over 9 files)",
"deviations": [
"Three runtime test files outside the claim's named file surface, each a test of a door this PR changes: automation-flow-credential-projection.test.ts (the automation-plane half of position 3), meta-list-read-gate-parity.test.ts (its double modelled "unknown type" as a throw; it now answers no list to reach the fallback exits), and the new meta-list-protocol-fault.test.ts.",
"A2 mechanism assumption partly falsified: no error type to discriminate exists (the one protocol answers an unknown type with an empty list), so every protocol throw propagates rather than a type-discriminated subset.",
"No change reaches a published package's exports: the new constants in flow-credential-projection.ts are not re-exported from @objectstack/service-automation's entry, and carryForwardRedactedValues keeps its signature (behaviour changed as described). Clause-②: no stands.",
"Beyond the claim's file surface for position 1: carryForwardRedactedValues gained the id-less-element anchor, needed so a withheld secret inside a parallel branch round-trips (inside the claimed metadata-redaction.ts file)."
],
"files_changed": [
".changeset/20590-flow-credential-positions.md",
"packages/metadata-protocol/src/metadata-redaction.ts",
"packages/metadata-protocol/src/protocol.metadata-redaction.test.ts",
"packages/runtime/src/domains/automation-flow-credential-projection.test.ts",
"packages/runtime/src/domains/meta-list-protocol-fault.test.ts",
"packages/runtime/src/domains/meta-list-read-gate-parity.test.ts",
"packages/runtime/src/domains/meta.ts",
"packages/services/service-automation/src/flow-credential-positions.test.ts",
"packages/services/service-automation/src/flow-credential-projection.ts"
],
"mcp_calls": "0: no MCP GitHub tool was called",
"api_writes": "3 REST writes, each ONE relay repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches, session token) executed as objectstack-fleet[bot]: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft #20615, body read back byte-identical, 12545 chars); (2) label-write assign -> PR #20615 assignee os-justin (read back: labels unchanged, assignee os-justin); (3) this os-dev-report comment -> POST /repos//issues/20590/comments. Plus 4 git pushes (not REST). Reads: REST GET of the issue, its comments, and the PR.",
"open_questions": [
{
"question": "Position 4: the specified shared-store composition is absent, but the plugin-absent composition exists in-repo (bootStack without automation:true, e.g. os verify). Does the card close with this PR, or stay open for position 4?",
"options": [
"A: keep "Fixes #20590" (the dispatch rule for "none"); the harness shape is recorded in Acceptance notes",
"B: the seat edits line 1 to "Part of #20590" and routes position 4 (a redactor present whenever flow rows are, e.g. bootStack honouring requires, or a built-in once spec declares the start config)"
],
"recommendation": "A. Business need: the plugin-absent composition measured is a verification harness whose store is private and seeded from the app's own source, so no secret live elsewhere is served, and no in-repo host shares a store across the two capability sets. Long-term soundness: a real remedy is a composition decision (a built-in redactor is blocked by Prime Directive #2 until spec declares the start config), not a patch in this PR. AI error prevention: nothing an author writes changes here. Startup focus: no pull for a new surface; B stays one edit away if the seat reads the harness as the composition."
}
],
"out_of_scope_findings": [
"class: b · reach: exception: could leak data · evidence: HttpConfigSchema.headers is an open z.record(string,string) (connectorConfig.input likewise), served verbatim by every flow definition read; a static credential typed into one is marked by a header or parameter name, not a declared key, so the table and the enumeration pin (declared keys only) do not cover it; no shipped example authors one · same family: name it for folding into this family's closeout (the fold rule), not a single card · dedupe words: flow http headers authorization served · open map credential header redaction · connectorConfig input credential",
"carrier: none · noted, not filed: the dispatcher /meta list still reaches the metadata service's unredacted list when the protocol slot has no list verb or answers no list; dormant (the one in-repo protocol always answers a list)",
"carrier: none · noted, not filed: the dispatcher item read swallows a protocol fault and falls to a getItem no in-repo metadata service implements, so a fault reads as 404; the dispatcher /published read swallows a layered-read fault and serves the (now redacting) code-layer snapshot. No credential served.",
"carrier: none · noted, not filed: in the verify-harness boot, the automation domain's definition read answered 404 for flows authored through /meta, so that door was not probed live (unit pins cover it); root cause NOT MEASURED",
"carrier: none · noted, not filed: a plugin-registered node kind that declares a credential key is outside the table; the enumeration reads builtin and spec-declared contracts only"
]
}
Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
ACCEPT — PR #20615 at
fee1d6b9·domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-09-29T08:22ZChecklist, read on GitHub rather than from the report:
main. 9 files, +1051 / −97. No governed path.Part of #20590. This seat made that edit; the reason is below.Clause-②: noholds.flow-credential-projection.tsare not re-exported.@objectstack/service-automationhas oneexportsentry (./dist/index.js).src/index.tsnames nothing from that module, and onlyplugin.tsimports it.carryForwardRedactedValueskeeps its signature./metalist change removes a fallback. It does not move an accept set.patch.Mapkeyed by node kind. It is not an object literal, so an author-controlledtypecannot resolve throughObject's prototype.FLOW_REGION_SLOTS_BY_TYPE, not a local copy.'') is served as written and never listed as withheld./metalist: a protocol throw is answered througherrorFromThrown. The metadata-service fallback is reached only by a protocol that has no list verb or answers no list.99a75023), then the fix (1f17293b), then the changeset (fee1d6b9).dist, for the runtime pin that readsmetadata-protocolfrom build output.statusandcode: 503SERVICE_UNAVAILABLEand 400INVALID_REQUEST. The undeclared-throw case asserts status 500 only. That case is a fault, not a refusal.runtimetest files outside the named surface. Each tests a door this PR changes.metadata-redaction.ts.This seat's dispatch assumption A2 was wrong, and is acknowledged here. It assumed the protocol signals an unknown type by throwing, so a type-discriminated subset of throws could keep falling through. In fact the one in-repo protocol answers an unknown type with an empty list. Every throw is a fault or a refusal, and every throw now propagates. The dev's measurement stands.
Measurements, stated abstractly per the disclosure rule:
bootStackloads automation only whenautomation: trueis passed. That harness's store is private and seeded from the app's own source, so no secret held anywhere else is served.Triage's p0 condition (
5884531404: p0 "if either is reached at a member-level read") is met by positions 1 and 3. This seat relays that to triage on #6015 and does not relabel.Why
Part of, notFixes. The report's first out-of-scope finding is a position in the same family that no declared-key table can cover. Under the fold rule it joins this card as position 5 instead of opening a single-point card, so the card must outlive this PR. After the merge, the seat posts theRelease:line and returns the card topm:queue.5. A credential typed into an open map in a flow node's config. Class b. Reach: the filing gate's may-leak-data exception, so measurement comes first.
HttpConfigSchema.headersis an open string map, and so isconnectorConfig.input.Clause-②.Findings, line by line:
/publishedserving the code-layer snapshot on a layered-read fault → Acceptance notes. No credential is served: fix(service-automation,metadata-protocol,metadata,runtime): withhold a flow's inbound-hook secret from every served definition, and keep it on a round trip (#20552) #20585 made that snapshot redacting./meta(root cause not measured) → Acceptance notes.Cross-lane: notices to
domain:engine(#6367, formetadata-protocol's carry-forward) and todomain:cli(#6024, forruntime's/metalist) follow.Landing waits for two things: every check green on the head, and the at-tier contract review, which is owed because the diff carries changeset prose.
Clause-②isno, so the PR gets noneeds:contract-reviewmarker.Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
Contract review
Served-tier:
CONTRACT_REVIEW_TIERHead-sha:
fee1d6b9bd465208ab551ceeb68095e4210443d3Local-runs: none
Inputs: card #20590 (body and all five comments — the fold
5884451279, the triage grade5884531404, the claim5885344488, the dev report5886359653, the seat's ACCEPT5886459470), PR #20615 (body, nine-file list, the net patch againstmainat the head), the head's check-runs, and — to verify the dev's factual claims — the unchanged files the diff depends on, read at the base tree. Nothing was built, run or re-run.① Derived judgments
Each accept-set or public-surface change the diff implies, and the verdict on it.
flowredactor withholds a second position, at every region depth.redactFlowCredentialsnow projectsFLOW_NODE_CREDENTIAL_KEYS(start→secret,http→signingSecret) and walksFLOW_REGION_SLOTS_BY_TYPEfrom@objectstack/spec/automation— the four slots the spec declares (loop.bodyone,parallel.branchesmany,try_catch.tryandtry_catch.catchone), read at the base. The served set narrows; the accept set is untouched (no schema and no write gate moved).redactedKeysstay index-addressed against the body handed in, the contract the write-side inverse reads. The table is aMap, so an author-controlledtypecannot resolve through the prototype. Right.HttpConfigSchema.signingSecretisz.string().optional(), so the empty string was already in the accept set: no widening and no spec change. Both outboxes (memory-http-outbox.ts,sql-http-outbox.ts) sign only on a truthy secret, so a cleared durable callout is delivered unsigned, as the changeset says. Right. The immediate arm is under ③ R2.carryForwardRedactedValues(public export of@objectstack/metadata-protocol, signature unchanged). Two behaviour changes: (a) grafts are collected against the untouched incoming body, the type's redactor is re-run over the grafted result, and a graft whose path the read would serve is dropped — the kept set only shrinks, so the loop terminates; (b) an array element with noidis walked by the first identified element beneath it on the same path, and an ambiguous match (two answering elements) grafts nothing.FlowSchemarefuses a duplicate node id across the top level and every region (the one-space rule inflow.zod.ts), so on a valid flow the anchor names at most one branch. Right. Position 3 (identity versus kind) is closed by (a): the dropped value is the author's word, and astartnode without a secret is refused at registration./meta/:typelist answers a protocol throw as itself.deps.errorFromThrown(e, 500)resolves the throw's own status and code (503SERVICE_UNAVAILABLE, 400INVALID_REQUEST; an undeclared throw is a 500 under the dispatcher's 5xx message withholding). The one in-repo protocol (getMetaItems→readFlattenedMetaItems) answers a type it holds nothing for with an empty list and never signals unknown-type by throwing, so no legitimate fall-through was lost. The metadata-service fallback remains for a protocol slot with no list verb or one answering no list. A200carrying unredacted stored bodies under a fault becomes the fault — a served-set correction, not an accept-set narrowing. Triage's option, alone, not both. Right.flow-credential-projection.ts(HTTP_SIGNING_SECRET_KEY,FLOW_NODE_CREDENTIAL_KEYS,FLOW_CREDENTIAL_CLEARED). The module is imported only byplugin.ts,src/index.tsnames nothing from it, and the packageexportsmap has the one.entry, so the module is not addressable. Not public surface — right.packages/spec/**path and no governed path; 1,148 changed lines. The path arm of the clause-② gate is not hit; the enumeration pin is a test in the owning package, not a gate.② Semver level
20590-flow-credential-positions.md:@objectstack/service-automationpatch,@objectstack/metadata-protocolpatch,@objectstack/runtimepatch. Matches the diff: no export added, removed or re-typed; three behaviour corrections behind unchanged signatures.metadata-protocolat patch is right — the [security] a flow's inbound-hook secret (config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #20552 changeset took minor when the identity walk arrived; this one adds no surface.Check Changesetis green on the head in both suites.Clause-②: no— on the PR body's line 3 and repeated in the changeset. Right: no accept set widened (J2's door was already accepted), no public surface expanded (J5), and not(narrowing): J4 replaces a wrong success under a fault with the fault, and nothing an author writes is refused that was accepted before.③ Boundary flags
Dev deviations (report
5886359653):5886459470. Answered.metadata-redaction.ts. Pinned: branch reorder, twin branches, the save-door round trip. Accepted.open_questions— position 4, A or B. The seat answered A for position 4 (the plugin-absent composition exists only in the verify harness, whose store is private and seeded from the operator's own source) and, separately, turned line 1 toPart of #20590because position 5 joined the card. The PR body readsPart of, and thePart-of PR must not also close its cardcheck is green. Answered; the card stays open on the seat's record. Disclosure: the PR body's position-4 note names a harness composition and an outcome — no request shape, no payload — and that composition serves nothing its operator does not already hold; this record reads it as within the rule, and the seat keeps the position-4 route abstract from here.out_of_scope_findings:/publishedserving the code-layer snapshot on a layered-read fault. No credential served: the snapshot redacts since fix(service-automation,metadata-protocol,metadata,runtime): withhold a flow's inbound-hook secret from every served definition, and keep it on a round trip (#20552) #20585. Acceptance notes; right./meta. Root cause unmeasured. Acceptance notes; right.Reviewer's own findings, not flagged by the dev:
nodes→ node id →config→body→nodes→ node id →config), so a node carried in the withheld form to a different region — lifted out of a loop body, or pushed into one — is not found under its stored container: nothing is grafted, the saved row holds nosigningSecret, and the next durable callout goes out unsigned with no refusal and no log line. Before this diff the key was served, so the same edit round-tripped it; the loss arrives with the fix, on the safe side (nothing is served). Triage's round-trip rule ("a write that carries the withheld form keeps the stored value") does not hold for this one edit shape, and the metadata-protocol test titled "a callout moved out of its region keeps its secret" moves nothing —per_rowstays inside the loop body — so that title overstates what is pinned. Remedy for the family's follow-up, in the same helper: an array hop whose stored element carries anidmay be resolved by that id anywhere in the body (ids are one space byFlowSchema), or the miss is logged once; and the test title is corrected. Recorded here for the seat to fold into the card.httparm never reads the signing secret.http-nodes.tsreadssigningSecretonce from the parsed config and hands it only toenqueueHttpon the durable arm; the immediatefetchsends the author's headers and no signature. So the cleared form is inert there, and the changeset's "durable callout" scope is exact. Pre-existing and outside this PR: the key's spec description promises a signature header without saying durable-only — a declared-versus-enforced note for the seat (a metadata-authoring trap candidate under Prime Directive chore: version packages #10), not this PR's to fix.content/docs/describes the flow credential withholding or the removal door; the changeset is the only prose, and the reference page is generated from an untouched spec. Nothing is advertised that is not delivered; a guide sentence is the docs lane's, not this PR's.Check-runs on the head: 41 check-runs, every one
completed: 36success, 5skipped(Build Docs,Console Pin Gate,Packed-tarball smoke (opt-in), and the body-edit suite'sAuto LabelandCheck PR Size). The seven required contexts aresuccess:Lint & Repo Gates,TypeScript Type Check(and its fourType Check ·jobs),Test Core(and shards 1–6),Dogfood Regression Gate(and shards 1–3),Build Core,Temporal Conformance (live PG + MySQL),Governed Surface Queue Guard. Also green:Check Changesetin both suites,Part-of PR must not also close its card,The card this PR closes must claim this branch, both claim-collision checks,Dogfood Verify CLI,Check Documentation Links,Flag docs affected by code changes. No non-green conclusion. The PR head was re-read asfee1d6b9after the last run completed; it is a draft ontomain, no auto-merge armed.Implemented-by:
claude/issue-20590-credential-positions-closeoutReviewed-by:
session_01XY5uCwTjZj7884yYtyur4HVERDICT: PASS
Generated by Claude Code
44 remaining items
objectstack-fleet commentedon Sep 29, 2026
ACCEPT, patch round 1 — PR #20672 at
695bb749·domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-09-29T17:04ZRead on GitHub (compare
992c9ac8...695bb749: 1 commit, 3 files):5894478795).urldescribe keeps its rule, and it routes by shape:restconnector withapi-keyauth (paramNameandauth.credentialRef);slackbot token).rest:mcp'sapplyAuthdoes not honourparamName.headersdescribe is unchanged.credentialRefroute.service-automation1913 passed;Landing waits for every check green on
695bb749and for an at-tier record on this head (dispatched now).Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
Contract review
Served-tier:
CONTRACT_REVIEW_TIERHead-sha:
695bb749a0fe12d9899e19109f2e4515349c296fLocal-runs: none
Inputs: card #20590 (body and all 29 comments — the fold
5884451279, the triage grade5884531404, the three PR #20615 rounds with their at-tier records and the landing5889550510, the regrade5889719353, the position-5 claim5889922486, its report5890660825and relay5890702006, the direction-A ruling5891721503, the claim5891908897, the round-0 report5894180071and ACCEPT5894227910, the at-tier record5894416988on992c9ac8, the round-1 dispatch5894478795, the round-1 report5894879143and ACCEPT5894911716), PR #20672 (body, four-file list, the two-commit net diff againstmainat the merge base9b402dba, and the one-commit delta992c9ac8..695bb749), the head's check-runs, and — to verify the dev's factual claims — the unchanged files the diff depends on, read at the merge base with read-onlygit show/git grep(the declarative connector auth schema and connector entry schema,resolveInstanceAuth, the rest, openapi and mcp providers' auth application, the slack connector and the showcase's registration of it, the flow credential projection, the specHttpConfigSchemadescribes, the automation route ledger, the form/Zod ledger test, the platform checklist fixture, the showcase connector instances). Nothing was built, run or re-run. The seat's ACCEPT was read as a comment, not adopted. The whole net diff is judged; the R1 delta is marked where it changes a judgment.① Derived judgments
httpdescriptor'sconfigSchemakeeps its key set (url,method,headers,body,durable,timeoutMs,signingSecret), itsrequired: ['url']and every type; twodescriptionstrings change, and the delta rewrites one of them again.HttpConfigSchemainpackages/specis untouched.io-node-form-zod-ledger.test.tscompares whether the two contracts agree on a closed value set, never description text, so it is unaffected by construction.AutomationEngine.getActionDescriptors()backsGET /automation/actions(runtime/src/domains/automation.ts; the route ledger maps it to the client'sautomation.listActions), so the text ships in@objectstack/service-automation'sdistand reaches the designer palette and any AI author reading the feed — the reader direction A names. A description is guidance to a consumer, not a contract term.signingSecretis withheld": verified. Right.flow-credential-projection.tsat the base:FLOW_NODE_CREDENTIAL_KEYSisstart→secret,http→signingSecret. The executor handscfg.urlandcfg.headersto the outbox or tofetchas authored (after interpolation). The describes, the code comment and the changeset state the served shape exactly; the diff withholds nothing — the ruling's ⛔ holds.urldescribe routes by shape, and every named route is carried at source. Right; closes the prior record's R1.connector_actionon a declarativerestconnector withapi-keyauth,paramNameandauth.credentialRef.ConnectorInstanceAPIKeyAuthSchema(shared/connector-auth.zod.ts) carriescredentialRef, optionalheaderNameand optionalparamName;DeclarativeConnectorEntrySchema.auth(integration/connector.zod.ts) takes that union and requiresprovider, and refuses an inline secret on every authored entry;resolveInstanceAuth(service-automation/src/plugin.ts) resolves the reference and passesparamNamethrough; the rest provider'sapplyAuthwritesquery[paramName] = keyandbuildUrlputs it on the URL's search params. The openapi provider does the same; the mcp provider is header-only and ignoresparamName— so namingrestis exact (one carried route named, not the only one).slackwith its bot token. The slack connector takes atokenoption and sends it as a bearerAuthorizationheader; the showcase supplies that token from the plugin's options inobjectstack.config.ts(an environment variable with a demo fallback), outside every flow definition;TaskCompletedSlackFlowis aconnector_actiononslack/chat.postMessage.ConnectorInstanceAuthSchemahas four variants —none,bearer,api-key,basic— and none carries a secret into the URL path, so the prior over-reach is gone and the sentence now names only routes that exist.headersdescribe is unchanged in the delta (zero diff lines), and its route —bearer/basic/api-keyby header — is carried by the same schema and the same providers.httpheaders / connector input / url describes route credentials tocredentialRef, and one exported predicate draws an author-time advisory on a credential-shaped literal (the spec half of #20590) #20654 brings to theHttpConfigSchemadescribes"; at the base the spec describes still readTarget URLandRequest headers, so the tense matches the tree. It also records why the path case has nocredentialRefroute. One residual wording point under ③ N3.flows/index.tsis a comment;url,method,bodyand the node type are as before (readable from the diff; the dev's byte-identical compile is the report's). The placeholder says plainly it is served and why a real one never goes there, and the branch comment no longer teaches the incoming-webhook route. Keeping thehttpnode is right:docs/qa/platform-checklist/areas/automation.jsonpinsparallel+http=showcase_fan_out_notifyinautomation.flow-node-type-matrix, andFanOutNotifyFlowis that flow (name verified at the head). Both routes the comment names were verified (J4); an unresolvedcredentialReffails the boot (resolveInstanceAuththrows, the caller fails the instance), which is why every showcase instance declaresauth: { type: 'none' }and whysrc/system/connectors/index.tsis outside the claimed surface.it.each(['url', 'headers'])reads the served descriptor throughengine.getActionDescriptor('http')afterregisterHttpNodes— the same object the feed serves — and assertsauth.credentialRefandconnector_action, not the prose. At the head both tokens sit in both descriptions; forurlboth now come from the query-string clause, as the test comment says. The test imports source, so no build sits in the path. The ablation and suite counts are the report's, not re-run here. See ③ N2.signingSecretis withheld). No request shape or payload for a reach appears; the one probe named in the body measures the descriptor feed. The round-1 report carries no reach measurement.#20590/#20654anchors sit in code comments; the two served description strings carry none.5891908897. Right. Four files:http-nodes.tsand its test, the showcaseflows/index.ts,.changeset/20590-http-credential-guidance.md. No governed path (Governed Surface Queue Guardsuccess); base-repository head, not a fork; 88 changed lines; draft, auto-merge not armed.mainhas moved 13 commits past the merge base; none touches the four files or any source this record read (connector auth, rest/slack/mcp/openapi providers, the projection, the showcase automation tree).Target URL/Request headerson the base is another object's field label (sys_webhook,sys_http_deliveryand their generated translations), an unrelated spec describe, or the reference generated from the untouched spec, so nothing outside the package can go red on this change.② Semver level
@objectstack/service-automation:patch— right. Two description strings in a served descriptor; no key added, removed or retyped; nothing newly withheld; nothing to migrate. Ships indist(filesisdist,README.md,CHANGELOG.md; noprivateflag). Notskip-changeset: a released package's published output changes.@objectstack/example-showcase:private: true(verified) — no changeset. Right.Clause-②: no— right, and matches the ruling. No new authorable key or accepted value anywhere in the diff; neither arm of the closed pair applies. PR body line 2 and the changeset body both carry it in the fixed spelling;Check Changesetis success in both suites on the head.auth.credentialRef; query key →rest+api-key+paramName+auth.credentialRef; path secret → a token-authenticated connector, and the sentence that nocredentialRefvariant carries a path secret). Abstract at the level the disclosure rule asks; no model identifier; the headline's tracker number follows a convention a quarter of the sibling changesets on the base use.③ Boundary flags
Round-0 dev report
5894180071:open_questions: none.urledited beside the claim-namedheaders. Accepted (J10; the ruling names the wholeconfigSchema).out_of_scope_findingsAdd metamodel interfaces for ObjectQL/ObjectUI contract #1 —flows.mdx's retired-shapes row still offers anhttpnode posting to an incoming webhook. The prior record escalated it as needing a carrier; the seat's round-1 dispatch5894478795records it filed as docs(automation): flows.mdx routes a secret-bearing webhook url toauth.credentialRef, which no connector auth variant can carry, and still offers anhttpnode posting to an incoming webhook as the Slack replacement #20682 (domain:devx). Under this review's input rule that card was not read; the carrier stands on the seat's record. Answered.Round-1 dev report
5894879143:open_questions: none.out_of_scope_findingsAdd metamodel interfaces for ObjectQL/ObjectUI contract #1 — the same retired-shapes row → docs(automation): flows.mdx routes a secret-bearing webhook url toauth.credentialRef, which no connector auth variant can carry, and still offers anhttpnode posting to an incoming webhook as the Slack replacement #20682 per the seat. Answered.out_of_scope_findings✨ Set up Copilot instructions #2 — the landed flows guide callout still routes a signed webhook url toauth.credentialRef, the over-reach this delta removed for the path case. The seat's dispatch records it under docs(automation): flows.mdx routes a secret-bearing webhook url toauth.credentialRef, which no connector auth variant can carry, and still offers anhttpnode posting to an incoming webhook as the Slack replacement #20682 and places wording guards on spec+lint: flowhttpheaders / connector input / url describes route credentials tocredentialRef, and one exported predicate draws an author-time advisory on a credential-shaped literal (the spec half of #20590) #20654 and skills(objectstack-automation): the http example routes an outbound credential tocredentialRefand stops teaching a secret-bearing webhook url (the skills half of #20590) #20657. Escalated with carriers; right. spec+lint: flowhttpheaders / connector input / url describes route credentials tocredentialRef, and one exported predicate draws an author-time advisory on a credential-shaped literal (the spec half of #20590) #20654 is the one place the corrected shape-split must land rather than the verbatim copy PR docs(automation): say an http node's url and headers are served; route credentials to connector credentialRef #20663 asked for.headersonly; cite the new head in Tests and Gates). The seat made it: item 1 carries the marked correction, and the appended round section cites695bb749with the readings. Answered.The prior record
5894416988:5894478795). Verified in the delta (J4): theurldescribe routes by shape, each route carried at source, theheadersdescribe untouched, the changeset aligned. Red-first and the ablation are the report's, not re-run here.auth.credentialRef, which no connector auth variant can carry, and still offers anhttpnode posting to an incoming webhook as the Slack replacement #20682 per the seat.Reviewer's own findings this round, not flagged by the dev:
credentialRefvariant that carries a secret into the URL path would be a spec widening (Clause-②: yes) and a family decision at spec+lint: flowhttpheaders / connector input / url describes route credentials tocredentialRef, and one exported predicate draws an author-time advisory on a credential-shaped literal (the spec half of #20590) #20654's face or on this card, not this PR's. Recorded for the seat to fold or strike.urldescribe no longer sends a path secret toauth.credentialRef), and nothing pins the negative; the pin holds the named route, as its comment says. A prose-absence pin would be brittle, so this record accepts the boundary as the test states it.auth.credentialRef(…api-key, whoseparamNameputs the key in the query)". That holds for the rest and openapi providers; the mcp provider ignoresparamName. The served describe namesrestand is exact; the comment is a one-word tightening the next touch of the file can take.Card state after landing:
Fixes #20590closes the card while #20654 (spec describes, predicate and advisory), #20657 (the skill) and #20682 (the docs rows) carry the remaining faces — the ruling's lane split (5891721503), the claim and the seat's round-1 dispatch say exactly this. Right.Check-runs on the head, read at 2026-09-29T17:13Z: 41 check-runs, every one
completed: 36success, 5skipped(Build Docs,Console Pin Gate,Packed-tarball smoke (opt-in), and the body-edit suite'sAuto LabelandCheck PR Size). The seven required contexts aresuccess:Lint & Repo Gates,TypeScript Type Check(and its fourType Check ·jobs),Test Core(and shards 1–6),Dogfood Regression Gate(and shards 1–3),Build Core,Temporal Conformance (live PG + MySQL),Governed Surface Queue Guard. Alsosuccess:Check Changesetin both suites,Part-of PR must not also close its cardin both,The card this PR closes must claim this branchin both, both claim-collision checks in both,Dogfood Verify CLI,Check Documentation Links,Flag docs affected by code changes,filter. No non-green conclusion, none in progress. The PR head re-read as695bb749on the PR itself: draft ontomainfrom the base repository, mergeable and clean, auto-merge not armed.Implemented-by:
claude/issue-20590-credential-guidanceReviewed-by:
session_01XY5uCwTjZj7884yYtyur4HVERDICT: PASS
Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
Landing — PR #20672 at
695bb749·domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-09-29T17:15Z5895049516is PASS on this head, and R1 is verified closed with every named connector route checked at source.695bb749, latest run per name: 29successand 5 expected skips. The PR is NOT governed: 88 changed lines.Fixes #20590as its only closing keyword.credentialRefvariant would be a spec widening, which is the family's decision and no measured producer needs it. The seat does not file it; it is recorded as the steer's boundary.paramNameto "a declarative connector", while the served describe namesrestexactly.Fixes #20590closes the card. The remaining faces are on spec+lint: flowhttpheaders / connector input / url describes route credentials tocredentialRef, and one exported predicate draws an author-time advisory on a credential-shaped literal (the spec half of #20590) #20654 (in flight), skills(objectstack-automation): the http example routes an outbound credential tocredentialRefand stops teaching a secret-bearing webhook url (the skills half of #20590) #20657 and docs(automation): flows.mdx routes a secret-bearing webhook url toauth.credentialRef, which no connector auth variant can carry, and still offers anhttpnode posting to an incoming webhook as the Slack replacement #20682.Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
Landed — the card closes ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-09-29T17:36ZPR #20672 merged through the merge queue as
14f80e23onorigin/main, andFixes #20590closed this cardcompleted. The at-tier PASS on the landed head is5895049516.This card's arc, for the record:
3f45b6cc), after three at-tier records and two patch rounds./metalist answers a protocol fault as itself.httpnode that setssigningSecretsends its request unsigned on the inline arm (and on the durable arm's no-outbox fallback), while the published contract promisesX-Objectstack-Signature#20628, the inlinehttparm now signs (PR fix(service-automation): sign the http node's inline request with the one scheme, and refuse a secret that did not resolve #20640,89801cd9).5891721503) is carried out by lane:httpdescriptor'surl/headerssteer, routed by credential shape, plus the showcase site;httpheaders / connector input / url describes route credentials tocredentialRef, and one exported predicate draws an author-time advisory on a credential-shaped literal (the spec half of #20590) #20654 (domain:spec, in flight): the spec describes plus the lint predicate and advisory;credentialRefand stops teaching a secret-bearing webhook url (the skills half of #20590) #20657 (domain:skills): the automation skill;auth.credentialRef, which no connector auth variant can carry, and still offers anhttpnode posting to an incoming webhook as the Slack replacement #20682 (domain:devx): the landedflows.mdxcallout's over-reach and its retired-shapes row. docs(automation): the flows guide routes anhttpnode's outbound credential to a connector'scredentialRef, and replaces its secret-bearing webhook-url example (the docs half of #20590) #20655 (the docs half) landed as PR docs(automation): say an http node's url and headers are served; route credentials to connector credentialRef #20663.5895049516), which would need a spec widening that is the family's to decide.In the same act, this seat removes
pm:dispatchedand the assignee.Generated by Claude Code
docs(spec): re-anchor the dead tracker citations in the packages/spec…
docs(runtime): re-anchor the dead tracker citations in packages/runti…
fix(service-automation,metadata-protocol,runtime): withhold the remai…
docs(automation): say an http node's url and headers are served; rout…
fix(service-automation): the http node's url and headers describes ro…
docs(skills): objectstack-automation routes an outbound credential to…
fix(lint,metadata-protocol)!: /meta refuses a secretless api flow, an…
feat(lint,spec): a credential typed as a literal into a served flow p…