Skip to content

SMOODEV-2392: th config set derives tier from schema, secret for unknown (ADR-075 WS1)#180

Merged
brentrager merged 1 commit into
mainfrom
SMOODEV-2392-config-set-derive-tier
Jul 8, 2026
Merged

SMOODEV-2392: th config set derives tier from schema, secret for unknown (ADR-075 WS1)#180
brentrager merged 1 commit into
mainfrom
SMOODEV-2392-config-set-derive-tier

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

th config set defaulted --tier public and never consulted the schema. Tier is a real security boundary (secret-tier is AES-encrypted at rest, B2M keys can't read secret tier, the ADR-074 app surface serves public-tier to anonymous callers), and the failure is asymmetric: a public key mislabeled secret fails loud, a secret mislabeled public fails silent. The 2026-07-08 audit found four live credentials stored tier=public. ADR-075 makes the schema authoritative.

Solution (ADR-075 WS1 / D6 — the CLI UX guardrail that ships with server Phase 0)

th config set:

  • Schema-derived tier default — when --tier is omitted, look the key up across the union of the org's remote schemas (public / secret / feature_flag / limit). Found → use the schema's tier. Declared in no schema → default secret (the loud-failure direction) with a warning. Conflicting tiers across schemas → secret wins (D1 migration tiebreak).
  • Contradiction guard — an explicit --tier that contradicts the schema tier is refused with an error naming the schema tier; --force overrides (still warns).
  • Credential tripwire — a public-tier write of a credential-shaped value (sk-, github_pat_, ghp_, xox, -----BEGIN, or a long three-part JWT) prompts for confirmation on a TTY and refuses non-interactively unless --force.
  • --help / module docs updated.

Server-side derivation + 400-on-contradiction (Phase 1) lands separately in WS2/the TS routes; this is the D6 CLI layer that ships with Phase 0.

Verification

cargo test -p smooai-smooth-cli — 337 passed. 12 new unit tests cover tier derivation across all four schema kinds, the secret-wins union tiebreak, bare-doc tolerance, every resolve_set_tier branch (schema default, unknown→secret, matching/contradicting explicit ± force), the credential-shape heuristic (incident classes + benign negatives), and the tripwire decision (force/tty/non-tty). Derivation, contradiction, and tripwire logic are pure functions. rustfmt --check clean; clippy shows only pre-existing workspace pedantic warnings.

Warnings go to stderr so --json stdout stays a clean wire shape.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LE7V35vzRiHyxBFE8wAxb4

…own (ADR-075 WS1)

`th config set` defaulted `--tier public` and never consulted the schema, so a
secret mislabeled public failed silently (the 2026-07-08 incident: four live
credentials stored tier=public). ADR-075 makes the schema authoritative.

`th config set` now:
- Derives the tier from the key's declaration across the org's schema union
  (public/secret/feature_flag/limit) when --tier is omitted; a key declared in
  no schema defaults to `secret` (the loud-failure direction) with a warning.
- Refuses an explicit --tier that contradicts the schema tier (naming it);
  --force overrides, still warning.
- Credential tripwire: a public-tier write of a credential-shaped value (sk-,
  github_pat_, ghp_, xox, -----BEGIN, long JWT) prompts on a TTY and refuses
  non-interactively unless --force.

Server-side enforcement (Phase 1) lands separately; this is the D6 UX guardrail
that ships with Phase 0. Pure derivation/contradiction/tripwire logic is unit
tested (12 tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LE7V35vzRiHyxBFE8wAxb4
@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: c50e983

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager brentrager enabled auto-merge (squash) July 8, 2026 23:12
@brentrager brentrager merged commit e4fd765 into main Jul 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant