Skip to content

docs(static-variables): add prompt-injection threat model and trust tiers#1035

Merged
dhruva-reddy merged 1 commit intodr/squads-passing-data-between-assistantsfrom
dr/static-variables-prompt-injection-hardening
Apr 29, 2026
Merged

docs(static-variables): add prompt-injection threat model and trust tiers#1035
dhruva-reddy merged 1 commit intodr/squads-passing-data-between-assistantsfrom
dr/static-variables-prompt-injection-hardening

Conversation

@dhruva-reddy
Copy link
Copy Markdown
Contributor

Description

Stacked on top of #1033 (dr/squads-passing-data-between-assistants). Targets the static-variables-and-aliases page; the squads page in #1033 stays scoped to its own concern.

Surfaced by an FDE conversation with Mudflap asking how to do progressive authentication on inbound calls without giving the LLM a path to forward a fake caller-ID. The current page documents the mechanics of static parameters correctly but doesn't frame it as a security boundary, and doesn't address the failure modes that break that boundary in practice. This PR adds that framing.

What changes in fern/tools/static-variables-and-aliases.mdx

  • Naming distinction added at the top: function.parameters (LLM-facing JSON schema, model fills it) vs. top-level parameters (server-merged, LLM never sees it). The dashboard surfaces both as similarly-named sections (just Parameters vs Static Body Fields), which is the most common cause of customers accidentally exposing trusted fields to the model.
  • Trust tiers for the Liquid variable bag. Tier 1 (server-trusted: customer.*, phoneNumber.*, transport.*, call.*, assistant.*, time variables, assistantOverrides.variableValues set at call start). Tier 2 (conversation-derived, not safe as a security boundary: messages, transcript, prompt). Tier 3 (LLM-derived, never trustworthy: variableExtractionPlan aliases from non-trusted sources, handoff arguments, handoff schema extraction).
  • Five common failure modes with bad/good code pairs: defining the trusted field in function.parameters; body-default leak; system-prompt forwarding; unsafe alias chains; mid-call bag mutation.
  • Worked example for caller-ID-based progressive authentication (the Mudflap pattern).
  • Tool-type support matrix plus an explicit warning that legacy assistant.model.functions[] does not support static parameters (the converter zeroes them out at request time, so customers on the deprecated shape have no orchestration-layer injection at all).
  • Handoff section cross-linking to /squads/passing-data-between-assistants (feat: add "Passing data between assistants" page to squads #1033). Documents that tool.parameters doesn't exist on handoff and explains why it doesn't need to: call-level Liquid variables persist across handoffs; aliases from server-trusted sources persist via allMessagesContext.variablesAdd; destination.assistantOverrides.variableValues is merged at handoff time bypassing the LLM. Flags that feat: add "Passing data between assistants" page to squads #1033's Approach 1 (handoff arguments via function.parameters) is correct for LLM-derived values like sentiment/intent but is not a security boundary for signaling-derived values like caller-ID.
  • Known limitation flagged: Liquid templates inside destination.assistantOverrides.variableValues are not currently resolved at handoff time (values spread verbatim into the bag).
  • Dashboard mental model section: how the API request and function tool forms surface the two parameters fields, with a step-by-step for the caller-ID pattern.

Skipped

  • Workflows extraction failure mode -- workflows are deprecated, intentionally not addressed.
  • test-writer / code-reviewer -- docs-only PR.

Testing Steps

  • fern check -- 0 errors
  • All JSON code blocks validate (15/15 OK)
  • All internal links resolve (5/5: /squads/passing-data-between-assistants resolves via the parent branch in this stack; /tools/code-tool, /tools/custom-tools, /tools/tool-rejection-plan, /api-reference/tools/create all resolve)
  • Verify the page renders correctly in the preview deployment, especially the trust-tier tables and the bad/good code-pair sections
  • Confirm the new section anchors render (#the-variable-bag, #forwarding-trusted-data-across-handoffs) so the cross-references inside the page resolve
  • Sanity-check the dashboard section against the live ToolsV2 form -- verify section labels still read "Parameters" and "Static Body Fields"
  • Style guide compliance (active voice, present tense, no marketing language)
  • Code examples use realistic placeholders (YOUR_API_KEY, etc.)

Related

…iers

Expands tools/static-variables-and-aliases.mdx with the security framing
that customers building progressive-authentication flows need:

- Lead with the naming distinction between function.parameters
  (LLM-facing JSON schema) and the top-level parameters array
  (server-merged, LLM-invisible). The dashboard surfaces both as
  similarly-named sections, which has bitten customers.
- Document the full Liquid variable bag in three trust tiers:
  Tier 1 server-trusted (customer.*, phoneNumber.*, transport.*,
  call.*, assistant.*, time, assistantOverrides.variableValues set
  at call start), Tier 2 conversation-derived (messages, transcript,
  prompt -- not safe as a security boundary), Tier 3 LLM-derived
  (variableExtractionPlan aliases from non-trusted sources, handoff
  arguments, handoff schema extraction).
- Five common failure modes with bad/good code pairs covering the
  patterns that defeat the security boundary (defining the trusted
  field in function.parameters; body-default leak; system-prompt
  forwarding; unsafe alias chains; mid-call bag mutation).
- Worked example for caller-ID-based progressive authentication.
- Tool-type support matrix, plus an explicit warning that legacy
  assistant.model.functions[] does NOT support static parameters.
- Cross-link to /squads/passing-data-between-assistants for the
  three-approaches handoff guide; flag the squads guide's Approach 1
  (function.parameters on handoff) as not-a-security-boundary for
  signaling-derived values.
- Document the known limitation that destination.assistantOverrides
  .variableValues is not Liquid-resolved at handoff time.
- Dashboard mental model: Parameters (JSON schema editor) vs Static
  Body Fields (key/value rows with Liquid).

Surfaced by Mudflap asking how to do progressive auth without giving
the LLM a path to forward a fake caller-ID.

Skipped: workflow-extraction failure mode (workflows are deprecated).
Skipped: test-writer / code-reviewer (docs-only PR, per write-pr rules).
@github-actions
Copy link
Copy Markdown
Contributor

@dhruva-reddy dhruva-reddy merged commit 3bbefdb into dr/squads-passing-data-between-assistants Apr 29, 2026
6 checks passed
dhruva-reddy added a commit that referenced this pull request Apr 29, 2026
…stic ≠ invisible (#1038)

Two combined goals -- the previous PR #1035 was orphaned by an inverted
merge order (parent #1033 squash-merged to main 25 seconds before child
#1035 squash-merged into the parent's branch, so #1035's content was
applied to a branch that was already obsolete and never propagated to
main). This restores that hardening AND fixes wording the user flagged
in review of the original page.

Recovers from orphaned commit 3bbefdb the full PR #1035 hardening
work: trust tiers (Tier 1 server-trusted, Tier 2 conversation-derived,
Tier 3 LLM-derived), five common failure modes, prompt-injection
threat-model framing, dashboard mental model, handoff-data-forwarding
section, legacy assistant.model.functions[] footgun warning, the
'Static parameters as a security boundary' section, and the worked
caller-ID example.

Wording fixes applied on top of that recovery, eliminating language
that conflated two different guarantees -- (1) static parameters are
truly LLM-invisible (server-merged into the request body, never in
the schema sent to the model) vs. (2) variableExtractionPlan aliases
chain values across tools deterministically but the source response
IS in the LLM's context (it was added to conversation history as a
role: tool message). Specific changes:

- Subtitle (line 3): split the two guarantees, no longer claims both
  are 'without LLM involvement'
- Intro bullet (line 19): 'deterministically -- the next tool gets
  the correct value regardless of how the LLM behaves between calls'
- Deterministic tool chaining intro (line 457): rewritten + new
  Warning callout explicitly stating 'Deterministic does not mean
  invisible' with the specific code-level claim that Tool A's
  response is added to the LLM's role:tool message history
- Tips section: new bullet 'Aliases are a determinism primitive,
  not an invisibility primitive', flagging that hiding values from
  the model requires the tool server to omit them from the response
  body in the first place

Skipped: test-writer / code-reviewer (docs-only PR).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants