Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,31 @@ versions are listed at

## Unreleased

### Breaking: workflow HTTP reads return summaries

The built-in workflow handler now returns `WorkflowRunSummary` from
`GET /runs`, `GET /runs/{runId}`, and the initial SSE `snapshot`. The
`useWorkflow` and `useWorkflowList` result and callback types use the same
summary contract.

These responses no longer contain run input, output, context, checkpoints,
source integration policy, node input and output, approval payloads and
decision metadata, or framework runtime metadata. List requests without an
explicit limit now read at most 100 runs.

`WorkflowClient` still returns the durable full run state for trusted
server-side code. If browser code reads a removed field, move that read to a
separately authorized server endpoint backed by `WorkflowClient`, and return
only the fields the application needs. Use `useApproval` or the dedicated
approval-by-ID route for approval payloads.

Operational error strings and approval request messages remain visible. Do not
place secrets, tokens, customer payloads, or private model output in those
developer-authored fields.

See [Workflows: loops, blob storage, React hooks](./docs/guides/workflows-advanced.md#understand-run-summaries)
for the exact summary shape and authorization guidance.

### Breaking: `veryfront dev` enforces CSRF

`security.csrf` now resolves the same way in every environment. Local
Expand Down
3 changes: 3 additions & 0 deletions docs/api-reference/veryfront/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ Options accepted by parallel.
| `WaitForEventOptions` | Options accepted by wait for event. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/dsl/wait.ts) |
| `Workflow` | Workflow instance | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/types.ts) |
| `WorkflowApprovalPendingEvent` | A pending approval was persisted; the run is parked until it is decided. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/events.ts) |
| `WorkflowApprovalSummary` | Data-minimized pending approval on the built-in HTTP surface. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/http/run-summary.ts) |
| `WorkflowBackend` | Public API contract for workflow backend. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/backends/types.ts) |
| `WorkflowClientConfig` | Configuration used by workflow client. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/api/workflow-client.ts) |
| `WorkflowContext` | Workflow context containing JSON-representable input and node outputs. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/types.ts) |
Expand All @@ -214,6 +215,7 @@ Options accepted by parallel.
| `WorkflowMetadata` | Public metadata captured for a registered workflow. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/registry.ts) |
| `WorkflowNode` | Workflow node | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/types.ts) |
| `WorkflowNodeConfig` | Union of all workflow node configurations | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/types.ts) |
| `WorkflowNodeStateSummary` | Data-minimized state for one workflow node on the built-in HTTP surface. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/http/run-summary.ts) |
| `WorkflowOptions` | Options accepted by workflow. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/dsl/workflow.ts) |
| `WorkflowRun` | Workflow run state | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/types.ts) |
| `WorkflowRunEvent` | A persisted workflow transition suitable for streaming to run observers. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/events.ts) |
Expand All @@ -222,6 +224,7 @@ Options accepted by parallel.
| `WorkflowRunObservation` | Atomic initial snapshot and ordered changes for one workflow run. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/backends/types.ts) |
| `WorkflowRunObservedState` | Minimal persisted run state used to derive public workflow events. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/backends/types.ts) |
| `WorkflowRunStatusEvent` | The run as a whole moved to a new status. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/events.ts) |
| `WorkflowRunSummary` | Data-minimized workflow run returned by the built-in HTTP and React surfaces. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/http/run-summary.ts) |
| `WorkflowRunUpdate` | Mutable run fields. On backends that declare `supportsRunPatchKeyMerge`, context and node-state entries merge by key atomically, so concurrent node outcomes cannot replace a sibling's persisted entry. Backends without that declaration replace the maps wholesale (the historical contract), so callers must send complete maps unless merge support was verified through `hasRunPatchKeyMergeSupport`. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/backends/types.ts) |
| `WorkflowStatus` | Public API contract for workflow status. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/schemas/workflow.schema.ts) |
| `WorkflowStepCompletedEvent` | A step finished successfully. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/workflow/events.ts) |
Expand Down
75 changes: 74 additions & 1 deletion docs/guides/workflows-advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ the browser request. Non-canonical route encodings are rejected before this
callback runs, so route-specific policies cannot authorize a different path
from the operation the handler dispatches.

The built-in handler does not apply per-run ownership filtering. Only authorize
an identity when it can read every run summary visible to the supplied client.
The approval-by-ID route returns the approval payload, so the identity must also
be allowed to read and decide those approvals. Use separate clients or separate
route authorization when users have different run visibility.

The handler covers every path the hooks call:

| Method | Path | Hook |
Expand All @@ -158,6 +164,73 @@ The handler covers every path the hooks call:
Mounting somewhere else means telling both sides. Pass `basePath` to the handler
and the matching `apiBase` to every hook.

### Understand run summaries

`GET /runs`, `GET /runs/{runId}`, and the first SSE `snapshot` frame return the
same `WorkflowRunSummary` shape. The built-in handler constructs this response
from an allowlist:

```ts
type WorkflowStatus =
| "pending"
| "running"
| "waiting"
| "completed"
| "failed"
| "cancelled";

type NodeStatus = "pending" | "running" | "completed" | "failed" | "skipped";

interface WorkflowNodeStateSummary {
nodeId: string;
status: NodeStatus;
attempt: number;
startedAt?: string;
completedAt?: string;
error?: string;
}

interface WorkflowApprovalSummary {
id: string;
nodeId: string;
status: "pending";
message: string;
requestedAt: string;
expiresAt?: string;
}

interface WorkflowRunSummary {
id: string;
workflowId: string;
version?: string;
status: WorkflowStatus;
currentNodes: string[];
nodeStates: Record<string, WorkflowNodeStateSummary>;
pendingApprovals: WorkflowApprovalSummary[];
createdAt: string;
startedAt?: string;
completedAt?: string;
error?: { message: string; nodeId?: string };
}
```

The summary omits run input, output, context, checkpoints, source integration
policy, node input and output, approval payload and decision metadata, and
framework runtime metadata. The dedicated approval-by-ID route remains the
explicit way for `useApproval` to fetch an approval payload.

Errors and approval request messages remain visible because the hooks and SSE
events use them for operations. Do not place secrets, tokens, customer payloads,
or private model output in developer-authored errors or approval messages. The
summary is data-minimized, not guaranteed secret-free.

`WorkflowClient` remains a trusted server-side API and returns the durable full
run state. Do not serialize its run values directly to a browser. If existing
browser code reads `run.input`, `run.output`, `run.context`, node payloads, or
approval payloads from `useWorkflow`, `useWorkflowList`, or the built-in run
routes, move that read to a separately authorized server endpoint. Select only
the fields the application needs. Use `useApproval` for approval payloads.

### Call the hooks across origins

A cross-origin `apiBase` needs CORS on both the preflight and the actual
Expand Down Expand Up @@ -282,7 +355,7 @@ allow credentialed CORS on the workflow origin. Native `EventSource` cannot set
an `Authorization` header. Bearer-token clients must use a fetch-based SSE
client and pass the same authorization header used by the workflow hooks.

The first frame is normally `snapshot`, using the same public run projection as
The first frame is normally `snapshot`, using the same run summary as
`GET /runs/{runId}`. When the stored run cannot be serialized, the stream opens
with a single `error` frame instead and closes; reconnecting re-reads the same
stored run, so that error is marked not retryable. Later frames use these
Expand Down
Loading
Loading