Skip to content

Add secrets CLI for typed app-config secret management #284

Description

@ChristianPavilonis

Problem

Downstream apps need a first-class way to manage secret-backed app config without storing plaintext secret material in config-store blobs or application config files.

Trusted Server has this exact need: several config fields are secret material today (publisher.proxy_secret, EC passphrase/secret key, handler passwords, partner API tokens, future integration API keys). See IABTechLab/trusted-server#684 for the app-level requirements and migration discussion.

EdgeZero already has adjacent app-config primitives:

  • AppConfigMeta::SECRET_FIELDS
  • SecretField
  • SecretKind::{KeyInDefault, StoreRef, KeyInNamedStore}
  • Runtime resolution via the app-config extractor's secret walk

But EdgeZero does not currently provide an operator workflow for setting/checking/deleting the actual secret-store values. config push should stay focused on config-store app-config blobs; secret-store mutation deserves an explicit CLI surface.

Proposal

Add an EdgeZero secrets command group with a small provider-aware CRUD/check surface:

edgezero secrets set <key>
edgezero secrets check <key>
edgezero secrets delete <key>

Downstream typed CLIs could expose the same commands, e.g.:

ts secrets set publisher/proxy_secret
ts secrets check publisher/proxy_secret
ts secrets delete publisher/proxy_secret

Command behavior

secrets set <key>

Interactive by default. It should:

  • resolve the target adapter and default [stores.secrets] store from edgezero.toml
  • prompt for the secret value without echoing it
  • optionally ask for confirmation / repeat entry for manual input
  • write the value to the provider secret store under <key>
  • support explicit non-interactive options later, e.g. --value-stdin, --from-env NAME, --from-file PATH, --store <id>, --adapter <name>, --local, --dry-run

Example:

edgezero secrets set publisher/proxy_secret
# prompts: Enter secret value for publisher/proxy_secret:

secrets check <key>

CI/deploy-friendly existence check. It should:

  • resolve the target provider secret store
  • verify that <key> exists and is readable/available, where the platform supports that
  • exit zero if present
  • exit non-zero if missing, inaccessible, or unsupported in a way that prevents verification
  • support --store <id>, --adapter <name>, and --local

Example:

edgezero secrets check publisher/proxy_secret

secrets delete <key>

Interactive/destructive by default. It should:

  • resolve the target provider secret store
  • require confirmation unless --yes is supplied
  • delete <key> from the provider secret store
  • support --store <id>, --adapter <name>, --local, --dry-run, and --yes

Example:

edgezero secrets delete publisher/proxy_secret

Relationship to typed app config

This command group can start as key-based provider operations. Later it can compose with typed app config descriptors to help operators discover/manage app-required secrets.

Possible future additions:

edgezero secrets plan --app-config trusted-server.toml
edgezero secrets check --all --app-config trusted-server.toml

But the initial command shape should stay simple and explicit: set/check/delete individual keys.

Desired typed-app integration later

EdgeZero should eventually have a reusable way for a typed app config to describe secret requirements:

  • config path / descriptor path, including nested fields and arrays
  • target secret store id/name
  • target secret key
  • whether the secret is required
  • whether inline authoring is allowed for migration/dev
  • whether the value can be generated
  • generation policy, if any

Example descriptor concepts:

pub struct SecretDescriptor {
    pub config_path: &'static str,
    pub default_store: Option<&'static str>,
    pub default_key: &'static str,
    pub required: bool,
    pub inline_policy: InlineSecretPolicy,
    pub generation: SecretGeneration,
}

pub enum InlineSecretPolicy {
    Allow,
    Warn,
    Reject,
}

pub enum SecretGeneration {
    None,
    RandomBase64 { bytes: usize },
    RandomHex { bytes: usize },
}

The descriptor mechanism should support nested paths and repeated items, e.g.:

  • publisher.proxy_secret
  • ec.passphrase
  • handlers.<id>.password
  • ec.partners.<source_domain>.api_token

Relationship to config push

Keep config push focused on the app-config blob. It may validate that secret references are structurally valid, but it should not implicitly mutate secret stores.

Recommended explicit workflow:

edgezero secrets set publisher/proxy_secret
edgezero secrets check publisher/proxy_secret
edgezero config push

This avoids surprising secret-store writes during config pushes and gives operators an explicit review step.

Provider considerations

EdgeZero should abstract provider differences, including:

  • Fastly Secret Store APIs / CLI behavior
  • Cloudflare Worker secrets / Secrets Store limitations
  • local Axum/dev environment secrets
  • platforms where listing/checking secrets is impossible or values are write-only

Where providers cannot read/list secret values, check can be existence-only or capability-specific.

Acceptance criteria

  • Add an EdgeZero secrets CLI command group.
  • Add secrets set <key> with interactive hidden input by default.
  • Add secrets check <key> with CI-friendly exit codes.
  • Add secrets delete <key> with confirmation by default.
  • Resolve target adapter and [stores.secrets] default from edgezero.toml.
  • Support --store <id> and --adapter <name> overrides.
  • Keep config push from implicitly writing secret-store values.
  • Document how this composes with existing AppConfigMeta::SECRET_FIELDS runtime resolution.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions