Summary
Business rules live in the CLI presentation layer (and the agent front door) instead of shared SDK
services, violating the project's own architecture rule:
CLAUDE.md: "Business logic must live in SDK/Core, never in the CLI presentation layer."
Because the logic isn't shared, copies drift — and that drift has already produced shipped bugs.
Why this matters now
The --repo confused-deputy class (multiple commands silently ignoring the global --repo flag and
acting on the default ~/.auths store — including a destructive reset --repo that wiped the wrong
store) was a direct symptom of this drift: storage-path resolution was duplicated across ~6 commands
instead of one shared helper, and the copies diverged so several forgot the flag. "Logic in the front door
drifts" is not hypothetical here — it was the root cause of a whole class of bugs.
Evidence
config — crates/auths-cli/src/commands/config.rs matches config keys, parses/validates values
(parse_cache_policy, parse_bool, duration parsing), and loads/saves config directly — all inline in
the command. CLAUDE.md explicitly names this leak: "Config management → should be in SDK as
ConfigService." Still present.
agent — agent env/lock/unlock/start/stop implement agent lifecycle in the command layer rather
than a shared service, so the agent front door carries the same problem.
- ~35 domain commands call no SDK capability at all (per a capability-mapping audit of
auths-cli/src/commands against the auths-sdk public surface), e.g. multi-sig begin/combine/sign,
approval grant/list, id bind-idp/migrate. This is a heuristic signal; config + agent are
confirmed by reading the source.
- The existing guard is only half the rule.
scripts/check_sdk_boundary.sh catches the CLI
importing auths_core/auths_id/auths_storage directly — but it does not catch domain logic
written inline in the CLI. So a command can branch on domain rules / parse / validate without importing a
lower crate and the guard passes clean (exactly how config.rs slips through).
Scope
- Affected: the CLI and the agent front doors.
- Not affected: the HTTP API (
auths-api, ~1.2k lines, no domain-logic patterns) is genuinely thin —
good, and the model to follow.
Proposed work
- Extract
config logic into an SDK ConfigService; the CLI config command becomes a thin translator.
- Move agent lifecycle (
env/lock/unlock, daemon management) into a shared SDK service.
- Consolidate storage-path resolution behind one helper used everywhere. (Partly done:
--repo is now
threaded through resolve_repo_path for several commands on the security-fix branch; finish the
remaining holdouts.)
- Add a logic-leak guard to CI — fail when a CLI command performs domain logic without delegating to
an SDK service. Extend check_sdk_boundary.sh beyond imports, or wire in a capability-coverage check
(a command that maps to zero SDK capabilities is a candidate leak).
- Triage the remaining ~35 no-SDK-capability commands: delegate to SDK, or document why each is
legitimately presentation-only.
Acceptance
config and agent business logic lives in SDK services; the CLI calls them.
- A CI check fails when a new CLI command embeds domain logic without an SDK call.
- The ~35-command list is triaged (delegated or justified with a reason).
Summary
Business rules live in the CLI presentation layer (and the agent front door) instead of shared SDK
services, violating the project's own architecture rule:
Because the logic isn't shared, copies drift — and that drift has already produced shipped bugs.
Why this matters now
The
--repoconfused-deputy class (multiple commands silently ignoring the global--repoflag andacting on the default
~/.authsstore — including a destructivereset --repothat wiped the wrongstore) was a direct symptom of this drift: storage-path resolution was duplicated across ~6 commands
instead of one shared helper, and the copies diverged so several forgot the flag. "Logic in the front door
drifts" is not hypothetical here — it was the root cause of a whole class of bugs.
Evidence
config—crates/auths-cli/src/commands/config.rsmatches config keys, parses/validates values(
parse_cache_policy,parse_bool, duration parsing), and loads/saves config directly — all inline inthe command. CLAUDE.md explicitly names this leak: "Config management → should be in SDK as
ConfigService." Still present.
agent—agent env/lock/unlock/start/stopimplement agent lifecycle in the command layer ratherthan a shared service, so the agent front door carries the same problem.
auths-cli/src/commandsagainst theauths-sdkpublic surface), e.g.multi-sig begin/combine/sign,approval grant/list,id bind-idp/migrate. This is a heuristic signal;config+agentareconfirmed by reading the source.
scripts/check_sdk_boundary.shcatches the CLIimporting
auths_core/auths_id/auths_storagedirectly — but it does not catch domain logicwritten inline in the CLI. So a command can branch on domain rules / parse / validate without importing a
lower crate and the guard passes clean (exactly how
config.rsslips through).Scope
auths-api, ~1.2k lines, no domain-logic patterns) is genuinely thin —good, and the model to follow.
Proposed work
configlogic into an SDKConfigService; the CLIconfigcommand becomes a thin translator.env/lock/unlock, daemon management) into a shared SDK service.--repois nowthreaded through
resolve_repo_pathfor several commands on the security-fix branch; finish theremaining holdouts.)
an SDK service. Extend
check_sdk_boundary.shbeyond imports, or wire in a capability-coverage check(a command that maps to zero SDK capabilities is a candidate leak).
legitimately presentation-only.
Acceptance
configandagentbusiness logic lives in SDK services; the CLI calls them.