# Flow composition A flow is a YAML file with three top-level keys: `inputs:`, `steps:`, `outputs:`. Read top to bottom, it tells the hub what kind of inputs to accept, which modules to call in what order, and what to surface as the final result. ## Smallest possible flow ```yaml name: hello description: Echo a name back as a greeting. inputs: name: type: text steps: - id: greet module: debug.echo inputs: payload: "Hello, {{ inputs.name }}" outputs: greeting: "{{ steps.greet.outputs.payload }}" ``` `chain run flows/hello.yaml --input name=World` produces: ```json { "greeting": "Hello, World" } ``` ## Real-world example: extract → summarize The bundled `flows/extract-summarize.yaml` chains two modules: ```yaml steps: - id: extract module: text.extract inputs: document: "{{ inputs.file }}" - id: summarize module: text.summarize inputs: text: "{{ steps.extract.outputs.text }}" style: "three bullet points" ``` The `{{ steps.extract.outputs.text }}` template plumbs the output of one step into the next. The flow engine type-coerces JSON / FileRef / Text per the destination module's manifest, so you don't need explicit conversion steps. ## Templating reference (short) | Expression | Resolves to | |------------------------------------|----------------------------| | `{{ inputs.X }}` | input named X | | `{{ steps.Y.outputs.Z }}` | output Z of step Y | | `{{ env.OPENAI_API_KEY }}` | env var (must be declared) | | `{{ now }}` | ISO-8601 UTC timestamp | | `{{ flow.execution_id }}` | per-run id (audit-log key) | ## Audit posture Every step emits four events: - `step.started` — module name, version, manifest hash - `step.completed` — duration, output size, well-known fields auto-elevated (`model_digest`, `engine`, …) - `step.failed` — error name + message - `flow.completed` — sum of step durations, total events All hash-chained into the same audit log. See the **Audit-log** explainer for the chain mechanics. ## What you'll see in this app - **Flows page**: list of saved flows under `~/.fai/data/flows/`. Click `Run`, fill any required inputs, watch the result. - **Audit page**: per-step events appear within seconds of a flow finishing. Filter on `flow.` to see only flow-level rows. - **Store**: any installed module shows up as available for `module:` in a flow YAML. Synthetic federated entries (`mcp..`, `n8n..`) work the same way.