diff --git a/docs.json b/docs.json index 3e54fff6..b2028f09 100644 --- a/docs.json +++ b/docs.json @@ -1053,6 +1053,7 @@ "qstash/howto/multi-region", "qstash/howto/local-development", "qstash/howto/local-tunnel", + "qstash/howto/redact-fields", "qstash/howto/signature", "qstash/howto/delete-schedule", "qstash/howto/reset-token", @@ -1286,6 +1287,7 @@ "workflow/howto/configure", "workflow/howto/cancel", "workflow/howto/security", + "workflow/howto/redact-fields", "workflow/howto/changes", "workflow/howto/schedule", "workflow/howto/use-webhooks", diff --git a/img/qstash/redact-logs.png b/img/qstash/redact-logs.png new file mode 100644 index 00000000..088b4754 Binary files /dev/null and b/img/qstash/redact-logs.png differ diff --git a/img/qstash/redact-schedules.png b/img/qstash/redact-schedules.png new file mode 100644 index 00000000..b4fea827 Binary files /dev/null and b/img/qstash/redact-schedules.png differ diff --git a/img/qstash/redact-workflow.png b/img/qstash/redact-workflow.png new file mode 100644 index 00000000..ebaae2a8 Binary files /dev/null and b/img/qstash/redact-workflow.png differ diff --git a/qstash/howto/redact-fields.mdx b/qstash/howto/redact-fields.mdx new file mode 100644 index 00000000..21fcb97d --- /dev/null +++ b/qstash/howto/redact-fields.mdx @@ -0,0 +1,132 @@ +--- +title: "Redact Private Data" +description: "How to redact private data in your messages" +--- + +QStash messages can contain private data that you don't want visible in the Upstash Console, or API responses. + +QStash allows you to redact specific fields so they appear as `REDACTED:` in the dashboard and API. The original values are still used when delivering messages to your endpoint. The SHA256 hash lets you verify the two data without revealing the original data. + +To redact a field, pass the `redactFields` option when publishing a message. + +Available options: +| Option | Description | +|--|--| +| body | Redact the body of the message | +| headers | Redact the headers of the message | +| headers[header_name] | Redact a specific header (e.g., `headers[Authorization]`) | + + +Redaction is one-way. Once a field is redacted, the original value cannot be retrieved from the API or dashboard. + + + +```typescript TypeScript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); + +const res = await client.publishJSON({ + url: "https://my-api...", + body: { hello: "world" }, + redactFields: { + body: true, + header: ["Authorization"] // or `header: true` to redact all headers + }, +}); +``` + +```python Python +from qstash import QStash + +client = QStash("") +client.message.publish_json( + url="https://my-api...", + body={ + "hello": "world", + }, + redact_fields={ + "body": True, + "header": ["Authorization"] // or `header: True` to redact all headers + }, +) +``` + +```bash cURL +curl -XPOST \ + -H 'Authorization: Bearer XXX' \ + -H "Content-Type: application/json" \ + -H "Upstash-Redact-Fields: body, header[Authorization]" \ + -d '{ "hello": "world" }' \ + 'https://qstash.upstash.io/v2/publish/https://my-api...' +``` + + + + + + +Redaction is configured per message, so you can redact different fields for different messages. + +If a message fails and moves to the DLQ, the redacted fields remain redacted in the DLQ. +However, when you retry a DLQ message, QStash delivers the original values to your endpoint. + +## Schedules + +You can also redact fields in schedules. Pass the `redactFields` option when creating a schedule, and all messages produced by that schedule will have the specified fields redacted. +Schedule get and list endpoints also apply redaction, so private data won't appear in API responses or the dashboard. + + +```typescript TypeScript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); +const res = await client.schedule.create({ + name: "my-schedule", + cron: "0 * * * *", + url: "https://my-api...", + body: { hello: "world" }, + redactFields: { + body: true, + header: ["Authorization"] + }, +}); +``` + +```python Python +from qstash import QStash + +client = QStash("") +client.schedule.create( + name="my-schedule", + cron="0 * * * *", + url="https://my-api...", + body={"hello": "world"}, + redact_fields={ + "body": True, + "header": ["Authorization"] + }, +) +``` + +```shell cURL +curl -XPOST \ + -H 'Authorization: Bearer XXX' \ + -H "Content-type: application/json" \ + -H "Upstash-Cron: * * * * *" \ + -H "Upstash-Redact-Fields: body, header[Authorization]" \ + -d '{ "hello": "world" }' \ + 'https://qstash.upstash.io/v2/schedules/https://example.com' +``` + + + + + + + + +When updating a redacted schedule via the dashboard or API, you must provide the original values for the redacted fields. + +If you don't provide the original values, the redacted fields will be saved as `REDACTED:` — which is the hash value visible in the dashboard, not the original data. + diff --git a/qstash/openapi.yaml b/qstash/openapi.yaml index 50a3db35..d06e70e7 100644 --- a/qstash/openapi.yaml +++ b/qstash/openapi.yaml @@ -783,7 +783,28 @@ paths: | Upstash-Failure-Callback-Timeout | Timeout for the callback request. Format is same as Upstash-Timeout header. | | Upstash-Failure-Callback-Retries | Number of retries for the callback request. Default is same as original message retries. | | Upstash-Failure-Callback-Retry-Delay | Retry delay for the callback request. Format is same as Upstash-Retry-Delay header. | - + + - name: Upstash-Redact-Fields + in: header + schema: + type: string + description: | + Comma-separated list of fields to redact from the schedule. Redacted fields appear as `REDACTED:` in the dashboard and API responses. The original values are still used when delivering messages to your endpoint. + + All messages produced by this schedule will have the specified fields redacted. Schedule get and list endpoints also apply redaction, so private data won't appear in API responses or the dashboard. + + Available options: + | Option | Description | + |--------|-------------| + | `body` | Redact the body of the message | + | `headers` | Redact all headers of the message | + | `header[header_name]` | Redact a specific header (e.g., `header[Authorization]`) | + + Examples: + - `body`: Redact only the body + - `body, header[Authorization]`: Redact the body and the Authorization header + - `body, headers`: Redact both body and all headers + requestBody: description: The raw request message passed to the endpoints as is content: @@ -1068,6 +1089,25 @@ paths: | Upstash-Failure-Callback-Retries | Number of retries for the callback request. Default is same as original message retries. | | Upstash-Failure-Callback-Retry-Delay | Retry delay for the callback request. Format is same as Upstash-Retry-Delay header. | + - name: Upstash-Redact-Fields + in: header + schema: + type: string + description: | + Comma-separated list of fields to redact from the message. Redacted fields appear as `REDACTED:` in the dashboard and API responses. The original values are still used when delivering messages to your endpoint. + + Available options: + | Option | Description | + |--------|-------------| + | `body` | Redact the body of the message | + | `headers` | Redact all headers of the message | + | `header[header_name]` | Redact a specific header (e.g., `header[Authorization]`) | + + Examples: + - `body`: Redact only the body + - `body, header[Authorization]`: Redact the body and the Authorization header + - `body, headers`: Redact both body and all headers + requestBody: description: The raw request message passed to the endpoints as is content: @@ -1727,7 +1767,26 @@ paths: | Upstash-Failure-Callback-Timeout | Timeout for the callback request. Format is same as Upstash-Timeout header. | | Upstash-Failure-Callback-Retries | Number of retries for the callback request. Default is same as original message retries. | | Upstash-Failure-Callback-Retry-Delay | Retry delay for the callback request. Format is same as Upstash-Retry-Delay header. | - + + - name: Upstash-Redact-Fields + in: header + schema: + type: string + description: | + Comma-separated list of fields to redact from the message. Redacted fields appear as `REDACTED:` in the dashboard and API responses. The original values are still used when delivering messages to your endpoint. + + Available options: + | Option | Description | + |--------|-------------| + | `body` | Redact the body of the message | + | `headers` | Redact all headers of the message | + | `header[header_name]` | Redact a specific header (e.g., `header[Authorization]`) | + + Examples: + - `body`: Redact only the body + - `body, header[Authorization]`: Redact the body and the Authorization header + - `body, headers`: Redact both body and all headers + requestBody: description: The raw request message passed to the endpoints as is content: diff --git a/workflow/howto/redact-fields.mdx b/workflow/howto/redact-fields.mdx new file mode 100644 index 00000000..8ffc36e0 --- /dev/null +++ b/workflow/howto/redact-fields.mdx @@ -0,0 +1,75 @@ +--- +title: "Redact Private Data" +description: "How to redact private data in your workflow runs" +--- + +Workflow runs can contain private data that you don't want visible in the Upstash Console or API responses. + +Upstash Workflow allows you to redact specific fields so they appear as `REDACTED:` in the dashboard and API. The original values are still used when delivering requests to your workflow endpoint. The SHA256 hash lets you verify the data without revealing the original values. + +To redact fields, pass the `redactFields` option when triggering a workflow run. + +Available options: +| Option | Description | +|--|--| +| body | Redact the body of the workflow steps | +| headers | Redact the headers of the workflow steps | +| headers[header_name] | Redact a specific header (e.g., `headers[Authorization]`) | + + +Redaction is one-way. Once a field is redacted, the original value cannot be retrieved from the API or dashboard. + + + +```typescript TypeScript +import { Client } from "@upstash/workflow"; + +const client = new Client({ token: "" }); + +const { workflowRunId } = await client.trigger({ + url: "https://my-app.com/api/workflow", + body: { hello: "world" }, + redactFields: { + body: true, + header: ["Authorization"] // or `header: true` to redact all headers + }, +}); +``` + +```python Python +from upstash_workflow import Client + +client = Client("") +client.trigger( + url="https://my-app.com/api/workflow", + body={ + "hello": "world", + }, + redact_fields={ + "body": True, + "header": ["Authorization"] // or `header: True` to redact all headers + }, +) +``` + +```bash cURL +curl -XPOST \ + -H 'Authorization: Bearer XXX' \ + -H "Content-Type: application/json" \ + -H "Upstash-Redact-Fields: body, header[Authorization]" \ + -d '{ "hello": "world" }' \ + 'https://qstash.upstash.io/v2/publish/https://my-app.com/api/workflow' +``` + + + + + + + +Redaction is configured per workflow run, so you can redact different fields for different runs. + +When `body` is redacted, the step outputs in the dashboard and API will show `REDACTED:` instead of the actual values. The workflow still executes with the original data. + +If a workflow run fails and moves to the DLQ, the redacted fields remain redacted in the DLQ. +However, when you retry, resume or restart a workflow run from DLQ, Workflow delivers the original values to your endpoint.