Skip to main content

Connect the CLI Through an Access Gateway

Use this setup when the public Synth URL is protected by Cloudflare Access or another gateway that requires extra HTTP headers. The CLI stays gateway-agnostic: you provide the headers required by your deployment through SYNTH_HTTP_HEADERS or the repeatable -H/--header option.

This feature requires synthkb-cli 0.5.2 or newer:

pipx upgrade synthkb-cli
synth --version

The version output should be synth, version 0.5.2 or newer.

How authentication is split

The gateway credential and the Synth API key have different jobs:

LayerCredentialHow the CLI sends itSaved by the CLI?
Access gatewayGateway-specific headers such as CF-Access-Client-Id and CF-Access-Client-SecretSYNTH_HTTP_HEADERS or -H/--headerNo
SynthA knowledge:read Synth API keyAuthorization: Bearer ...Yes, in the OS keychain or private credential file

Do not add Authorization to the custom header map. The CLI owns that header and rejects attempts to override it.

Set one JSON object for the current shell, then use the CLI normally. These values are placeholders; load real values from your secret manager instead of putting them in a checked-in script:

export SYNTH_HTTP_HEADERS='{"CF-Access-Client-Id":"replace-with-client-id","CF-Access-Client-Secret":"replace-with-client-secret"}'

synth login https://synth.example.com/
synth projects
synth use PROJECT_NAME_OR_ID
synth status
synth search "test query"

At the hidden API key prompt, paste only the raw Synth API key. Do not add a Bearer prefix. The CLI normalizes the public server URL to its MCP endpoint, sends the gateway headers to readiness and MCP requests, and sends the Synth key separately.

SYNTH_HTTP_HEADERS applies to every synth process started from that shell. The CLI deliberately does not copy gateway secrets into its saved connection, so load the variable again in every new shell or CI job.

When finished, remove the gateway values from the shell environment:

unset SYNTH_HTTP_HEADERS

Build the JSON from Bash variables

If a secret manager exports the two Cloudflare values, jq can construct valid JSON without relying on manual quoting:

export CF_ACCESS_CLIENT_ID='replace-with-client-id'
export CF_ACCESS_CLIENT_SECRET='replace-with-client-secret'

export SYNTH_HTTP_HEADERS="$(
jq -cn \
--arg client_id "$CF_ACCESS_CLIENT_ID" \
--arg client_secret "$CF_ACCESS_CLIENT_SECRET" \
'{"CF-Access-Client-Id":$client_id,"CF-Access-Client-Secret":$client_secret}'
)"

unset CF_ACCESS_CLIENT_ID CF_ACCESS_CLIENT_SECRET

PowerShell

PowerShell can create the JSON without manual escaping:

$env:CF_ACCESS_CLIENT_ID = 'replace-with-client-id'
$env:CF_ACCESS_CLIENT_SECRET = 'replace-with-client-secret'

$env:SYNTH_HTTP_HEADERS = (@{
'CF-Access-Client-Id' = $env:CF_ACCESS_CLIENT_ID
'CF-Access-Client-Secret' = $env:CF_ACCESS_CLIENT_SECRET
} | ConvertTo-Json -Compress)

Remove-Item Env:CF_ACCESS_CLIENT_ID, Env:CF_ACCESS_CLIENT_SECRET

synth login https://synth.example.com/
synth projects

Remove the header map when finished:

Remove-Item Env:SYNTH_HTTP_HEADERS

One-off headers

For a single command, put repeatable -H/--header options before the command:

synth \
-H "CF-Access-Client-Id:$CF_ACCESS_CLIENT_ID" \
-H "CF-Access-Client-Secret:$CF_ACCESS_CLIENT_SECRET" \
doctor

This form affects only that invocation. It does not persist the headers for the next command, so repeat the options or use SYNTH_HTTP_HEADERS. Prefer the environment form for secrets because command arguments can be visible in shell history and process inspection.

CI example

Store the Synth key and gateway values as separate CI secrets. This GitHub Actions example creates the JSON for the job without putting secret values in the command arguments:

- name: Query Synth through the access gateway
shell: bash
env:
SYNTH_API_KEY: ${{ secrets.SYNTH_API_KEY }}
CF_ACCESS_CLIENT_ID: ${{ secrets.CF_ACCESS_CLIENT_ID }}
CF_ACCESS_CLIENT_SECRET: ${{ secrets.CF_ACCESS_CLIENT_SECRET }}
run: |
set -euo pipefail
umask 077
printf '%s' "$SYNTH_API_KEY" > "${RUNNER_TEMP}/synth-api-key"
export SYNTH_HTTP_HEADERS="$(
jq -cn \
--arg client_id "$CF_ACCESS_CLIENT_ID" \
--arg client_secret "$CF_ACCESS_CLIENT_SECRET" \
'{"CF-Access-Client-Id":$client_id,"CF-Access-Client-Secret":$client_secret}'
)"
export SYNTH_CONFIG_DIR="${RUNNER_TEMP}/synth-cli"

synth login https://synth.example.com/ \
--api-key-file "${RUNNER_TEMP}/synth-api-key" \
--project PROJECT_NAME_OR_ID \
--credential-store file \
--non-interactive \
--json
synth status --json

See CLI Automation and CI/CD for cleanup, credential storage, JSON parsing, and retry guidance.

Security behavior

  • Custom headers are never written to the saved Synth connection.
  • Header values and the Synth API key are redacted from diagnostics and remote error messages.
  • Authorization, Host, request IDs, and content-negotiation headers owned by the CLI cannot be overridden.
  • Use a secret manager and short-lived gateway credentials where possible.
  • Do not send real values in screenshots, shell transcripts, support tickets, or checked-in environment files.

Troubleshooting

Run synth doctor --json with the header environment still loaded.

SymptomLikely cause and action
Cloudflare login HTML, gateway 403, or an access-denied pageThe gateway headers are missing, expired, or rejected. Reload SYNTH_HTTP_HEADERS and verify the service token policy.
Login works, but the next command fails at the gateway-H/--header was used only for login. Repeat it for every invocation or switch to SYNTH_HTTP_HEADERS.
Gateway passes the request, but Synth reports authentication failureReplace or reauthorize the Synth API key. Gateway credentials do not replace the Synth key.
SYNTH_HTTP_HEADERS must be a JSON objectValidate the environment value as JSON and ensure every header name and value is a string.
Additional HTTP headers cannot override ...Remove reserved headers such as Authorization or Host; let the CLI create them.
No such option: -HUpgrade to synthkb-cli 0.5.2 or newer, and confirm the selected executable with command -v synth or Get-Command synth.

For all commands and transport options, see the Synth CLI reference.