Skip to content

Standardize config input: --config means two different things across gateway and outpost #347

Description

@leggetter

Why

--config means two different things depending on which command group you are in.

Input format
hookdeck gateway source|destination create|upsert|update A JSON object string: --config '{"url":"..."}'
hookdeck outpost destination create|update Repeatable key=value: --config url=...

Same flag name, same conceptual slot, incompatible input. A user who learns one and tries the other gets an error rather than a result. --config-file is consistent across both; only --config diverges.

This was found while building the Outpost commands (#346) and is worth fixing deliberately rather than leaving as an accident of history.

What we found

Investigating why the two ended up different turned up a real asymmetry:

  • Outpost destination config is entirely flat, and every value is a string. All 9 destination types, 38 field definitions, no nesting. Worth noting: the /destination-types endpoint reports some fields as key_value_map, checkbox or select, but those are form-rendering hints, not wire types — the API accepts and stores every value as a string. Sending custom_headers as an object returns it normalised to a JSON-encoded string, byte-identical to sending it as a string.
  • Gateway config nests, one level and mechanically: auth.type, auth.token, auth.username, auth.webhook_secret_key, and so on.
  • No config key in either product contains a dot.

So the two designs are each locally reasonable — flat key=value cannot express gateway's nesting, and JSON is clumsy for Outpost's flat string fields. But that is an argument for a better shared mechanism, not for keeping two.

Proposal: dotted-path key=value

Adopt --config key=value everywhere, where the key may be a dotted path:

# gateway, today
hookdeck gateway destination create --name api --type HTTP \
  --config '{"auth":{"type":"BEARER_TOKEN","token":"xyz"}}'

# gateway, proposed
hookdeck gateway destination create --name api --type HTTP \
  --config auth.type=BEARER_TOKEN --config auth.token=xyz

# outpost — unchanged, flat is just the trivial case
hookdeck outpost destination create --tenant-id acme --type webhook \
  --config url=https://example.com/hooks

Three properties make this the right shared mechanism:

  1. It expresses everything either product needs, now and later. This matters most for Outpost, where destination types are defined by the deployment rather than the CLI: if a nested type ships tomorrow, --config a.b=c expresses it with no CLI release. A flat-only scheme would leave that type uncreatable until we shipped a fix, which is exactly what not hardcoding server-owned schemas is supposed to prevent.
  2. It is an existing convention, not an invention. Helm's --set a.b.c=v with --values file.yaml as the escape hatch is the same shape as --config a.b=v with --config-file.
  3. It costs nothing today. No config key in either product contains a dot, so there is no ambiguity to resolve up front.

What stays

  • --config-file keeps working in both, as the escape hatch for anything awkward inline.
  • Gateway's flat flags (--url, --bearer-token, --rate-limit) stay. They are sugar over the same config and are more discoverable wherever the CLI already knows the schema; Helm keeps both too. Only the JSON-string form of --config is affected.

Migration

--config '{...}' to --config a.b=v is breaking for anyone scripting gateway today, so:

  • Gateway --config accepts both forms during the transition — JSON when the value starts with {, dotted key=value otherwise. That standardizes the CLI in a minor release with nothing breaking.
  • The JSON form is documented as deprecated and removed at the next major.

Inventory

  • Outpost: support dotted paths in --config / --credential (no behaviour change for existing flat keys)
  • Gateway: accept dotted key=value in --config alongside the existing JSON form
  • Decide and document escaping for a literal dot in a key (a\.b), even if nothing needs it yet
  • Decide whether array indexing (a[0]=b) is needed — Outpost currently uses comma-separated strings, so this may be unnecessary
  • Deprecation notice on the JSON form of gateway --config
  • Document the rule in AGENTS.md: config-shaped input is --config key=value with dotted paths for nesting, plus --config-file; flat flags are sugar where the schema is CLI-known
  • Update REFERENCE.md and README examples
  • Next major: remove the JSON form

Open question

Whether to rename the Outpost flag in the meantime. Keeping --config means carrying a known collision until gateway accepts both forms; the Outpost commands are in beta and have no users yet, so renaming is cheapest now if we would rather not carry it. Landing the gateway dual-format support instead closes the collision without a rename.

Prior art

  • #346 — Outpost CLI epic, where this surfaced
  • AGENTS.md §2 "OpenAPI to CLI Conversion Standards" — currently prescribes flat flags and comma-separated arrays; this adds the case it does not cover, where the schema is owned by the server rather than the CLI
  • Helm --set / --values — the closest established convention

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions