The webhook destination docs give this as the signature header template:
DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATE="t={{.Timestamp.Unix}},v0={{.Signatures | join \",\"}}"
Go's template parser rejects a backslash inside an action:
raw: t={{.Timestamp.Unix}},v0={{.Signatures | join \",\"}}
PARSE ERROR: template: x:1: unexpected "\\" in operand
raw: t={{.Timestamp.Unix}},v0={{.Signatures | join ","}}
parses OK
So whether the documented value works depends entirely on what parses the line before Go sees it:
| how it's set |
backslashes |
result |
| shell, double-quoted |
stripped by the shell |
works |
| Config API JSON body |
unescaped by the JSON parser |
works |
| YAML, double-quoted |
unescaped by the YAML parser |
works |
.env file (godotenv — Outpost's own config loading — or Compose env_file:) |
unescaped in double-quoted values |
works |
docker run --env-file |
passed through literally |
invalid template |
docker-compose environment: |
passed through literally |
invalid template |
| YAML, single-quoted scalar |
passed through literally |
invalid template |
(Corrected after testing: the original version of this issue claimed .env files pass backslashes through literally. Most .env readers — godotenv, which Outpost itself uses, and Compose env_file: — unescape \" inside double-quoted values, so the documented line works there; only docker run --env-file is fully literal. A plain YAML scalar can't carry these values at all — they start with {{.)
The bottom rows are ordinary ways to configure a self-hosted deployment. Copy the documented line into any of them and the template is invalid — which, before #1020's fix, panicked the delivery worker at the first delivery, and now fails startup.
The Go default is unaffected — config.go:186 is a source literal that resolves to join ",".
Suggested change
Show the template value itself, and treat escaping as a property of the transport rather than of the template:
The header template is:
t={{.Timestamp.Unix}},v0={{.Signatures | join ","}}
This is the literal template value. If the format carrying it requires escaping — the inner quotes in a JSON string or a double-quoted shell string — apply that format's own escaping.
That way the reader is given the real value once, and escaping stays the transport's concern rather than something the docs must enumerate per context.
Also: the two field sets are mutually exclusive
Separate from the escaping, the docs list content-template and header-template fields in two tables. Both are accurate, but nothing indicates that borrowing a field from the other list is an error rather than an empty value.
One table makes the exclusivity structural instead of something the reader has to infer by comparing two lists:
| Field |
Content template |
Header template |
.EventID |
yes |
yes |
.Topic |
yes |
yes |
.Timestamp |
yes |
yes |
.Body |
yes |
— |
.Signatures |
— |
yes |
With a line under it stating that a field marked — is not merely empty — it makes the configuration invalid.
Scope
docs/content/destinations/webhook.mdoc — the tables and both the Managed and Self-Hosted examples
docs/content/self-hosting/changelog/upgrade-v0.12.mdoc — carries the same escaped value
The webhook destination docs give this as the signature header template:
DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_TEMPLATE="t={{.Timestamp.Unix}},v0={{.Signatures | join \",\"}}"Go's template parser rejects a backslash inside an action:
So whether the documented value works depends entirely on what parses the line before Go sees it:
.envfile (godotenv — Outpost's own config loading — or Composeenv_file:)docker run --env-filedocker-composeenvironment:(Corrected after testing: the original version of this issue claimed
.envfiles pass backslashes through literally. Most.envreaders — godotenv, which Outpost itself uses, and Composeenv_file:— unescape\"inside double-quoted values, so the documented line works there; onlydocker run --env-fileis fully literal. A plain YAML scalar can't carry these values at all — they start with{{.)The bottom rows are ordinary ways to configure a self-hosted deployment. Copy the documented line into any of them and the template is invalid — which, before #1020's fix, panicked the delivery worker at the first delivery, and now fails startup.
The Go default is unaffected —
config.go:186is a source literal that resolves tojoin ",".Suggested change
Show the template value itself, and treat escaping as a property of the transport rather than of the template:
That way the reader is given the real value once, and escaping stays the transport's concern rather than something the docs must enumerate per context.
Also: the two field sets are mutually exclusive
Separate from the escaping, the docs list content-template and header-template fields in two tables. Both are accurate, but nothing indicates that borrowing a field from the other list is an error rather than an empty value.
One table makes the exclusivity structural instead of something the reader has to infer by comparing two lists:
.EventID.Topic.Timestamp.Body.SignaturesWith a line under it stating that a field marked
—is not merely empty — it makes the configuration invalid.Scope
docs/content/destinations/webhook.mdoc— the tables and both the Managed and Self-Hosted examplesdocs/content/self-hosting/changelog/upgrade-v0.12.mdoc— carries the same escaped value