# Spec: Runtime Profiles ## Status Partially implemented. Conformance covers CRUD, enable/disable, and basic variable application at the CLI level (`conformance/cli/profile_test.go`). This spec adds the layered defaults precedence (global, workspace, and selected profile), webhook-header profile selection, webhook profile tokens, and how a selected profile's entries interact with a DAG's own `env:` and `secrets:` fields. ## Scope This spec covers how a runtime profile's variables and secrets are composed into a DAG-run's environment, across three layers: - Global defaults, set once for the whole server. - Workspace defaults, set per workspace and applied only to runs whose DAG carries a matching `workspace` label. - The explicitly selected profile, named by `--profile`, the `profile` field on `POST /dag-runs`, or (for a webhook-triggered run) the `X-Dagu-Profile` header or the profile bound to the webhook token. It also covers precedence against a DAG's own `env:` and `secrets:` fields, and masking of profile secret values. It does not define the workspace-managed secret registry (`ref:`-style secret entries, see spec 069), profile CRUD/enable-disable/basic variable application (already covered by `conformance/cli/profile_test.go`), or webhook HMAC signing. ## Goal Workflow authors and operators can supply shared configuration and secrets to DAG-runs from outside the DAG file, at different scopes (global, workspace, or per-run), without editing every DAG that needs them. ## Behavior A run's final environment composes, in order (later entries override earlier ones with the same name): 1. Global default profile variables. 2. Global default profile secrets. 3. Workspace default profile variables and secrets, applied only when the DAG's `labels` include a `workspace` entry naming a workspace with configured defaults. 4. The DAG's own `env:` field. 5. The selected profile's variables. 6. The selected profile's secrets. 7. The DAG's own `secrets:` field. A DAG's own `secrets:` field is therefore the final, highest-precedence layer: it overrides a same-named entry from any profile layer. The selected profile overrides the DAG's own `env:` field. Global and workspace defaults are managed only through the HTTP API (`/profiles/_global/...` and `/profiles/_workspaces/{name}/...`); no CLI command reaches them. A DAG `secrets:` entry with the `env` provider resolves its `key` against layers 1 through 6 in the same order, so it can read any profile variable or secret. A webhook-triggered run authenticated with the webhook's default token selects a profile with the `X-Dagu-Profile` request header, restricted to the profiles an admin allow-listed for that webhook (`PUT /dags/{fileName}/webhook/profile-selection`). Without the header, the DAG's default profile resolution applies. An admin can also create profile tokens for a webhook (`POST /dags/{fileName}/webhook/profile-tokens`), each bound to one profile. A run triggered with a profile token always uses that token's profile; the allow-list does not apply. Profile tokens are ignored while the webhook authenticates with HMAC only. A resolved profile secret is masked in run output exactly like a DAG-level secret: the literal value is replaced with `*******` in the step's stdout and stderr log files, the rendered status tree, and stored run status. ## Errors A webhook request naming a profile outside its configured allow-list is rejected with `403` and does not create a DAG-run. A webhook request authenticated with a profile token whose `X-Dagu-Profile` header names a different profile is rejected the same way. A revoked profile token is rejected with `401`. An unresolvable selected profile (unknown name, or disabled) fails the run before any step starts. ## Examples ```yaml labels: - workspace: acme steps: - command: echo "$SHARED_VAR" ``` Selecting a profile for a local run: ```sh dagu start --profile=deploy-prod my-dag.yaml ```