Workflow User Guides¶
Orchestrator can serve a Markdown guide alongside a workflow, for operators who need instructions on how to run it or what to expect. This mirrors the way translations are served: a directory of files is pointed to by an application setting, and a REST endpoint reads the file matching the requested name.
Configuring the guide directory¶
Set WORKFLOW_USER_GUIDE_DIR in your application settings to the directory that holds
your guide files:
from pathlib import Path
from orchestrator.core.settings import app_settings
app_settings.WORKFLOW_USER_GUIDE_DIR = Path("docs/workflow-guides")
If WORKFLOW_USER_GUIDE_DIR is None (the default), the endpoint returns 404 for
every workflow.
Use a directory managed by trusted administrators, preferably mounted read-only for the application. Path containment checks reject traversal and external symlinks, but do not protect against an untrusted local writer replacing files between validation and opening them. Only put content intended for guide readers in this directory.
Guide file layout¶
Each guide is a single Markdown file, named after the workflow it documents:
docs/workflow-guides/
├── create_l2vpn.md
├── modify_l2vpn.md
└── terminate_l2vpn.md
get_workflow_guide(workflow_name) reads {WORKFLOW_USER_GUIDE_DIR}/{workflow_name}.md
as UTF-8 text and returns its contents, or None if the directory isn’t configured or
the file doesn’t exist. Workflows without a corresponding file simply have no guide.
REST endpoint¶
The guide is served at:
GET /api/workflow_user_guides/{workflow_name}
This endpoint requires authentication, unlike the public /api/translations endpoint,
since guide content is considered more sensitive. It uses the application’s configured
authentication and authorization; disabling authentication also makes guides accessible
without credentials. It returns:
200with the guide’s Markdown content, if the file exists404if no guide is configured or found for that workflow name, or if the name resolves outsideWORKFLOW_USER_GUIDE_DIR(including external symlinks). Missing and rejected guides return the same response to avoid revealing which names are external symlinks.422ifworkflow_namecontains characters outsideSafeName’s allowlist (^[A-Za-z0-9._/-]+$, fromnwastdlib.file_utils)
workflow_name must fulfil SafeName and is a single path segment: guides are looked up
directly in WORKFLOW_USER_GUIDE_DIR, not in subdirectories. A request such as
/api/workflow_user_guides/nested/guide does not match the route and returns 404.
The response contains a JSON string, not rendered HTML. Clients that render the Markdown must sanitize HTML and unsafe links before displaying it.
orchestrator.core.services.workflow_user_guides
get_workflow_guide
async
get_workflow_guide(workflow_name: str | os.PathLike[str]) -> str | None
Return the Markdown guide for a workflow, or None if not found.
The guide file is resolved inside WORKFLOW_USER_GUIDE_DIR; this function does not assume that
workflow_name has already been validated. Path resolution is synchronous; the file check and read
use the asynchronous wrappers provided by anyio.Path.
Raises:
-
PathOutsideRootError–If
workflow_nameresolves to a path outside ofWORKFLOW_USER_GUIDE_DIR.
Source code in orchestrator-core/orchestrator/core/services/workflow_user_guides.py
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |