15 KiB
Two Auth Modes Implementation Plan
Intent: Схлопнуть Lemana VPN до двух понятных пользовательских режимов: ручной вход с сохранёнными LDAP login/password в macOS Keychain и старый личный automatic path через Bitwarden + Touch ID + полный autofill/submit.
Current Behavior: Checkout уже manual-first, но публичный контракт всё ещё размазан по manual, saved-login, saved-totp, auto, совместимым alias-командам и status/menu available_modes. Локально установленный ~/bin/vpn-lemanapro.sh отстаёт от checkout, поэтому старые сборки и приложение могут видеть другую схему.
Expected Outcome: Installer, CLI, app menu, README и smoke tests показывают ровно два режима: manual-keychain и bitwarden-auto. Первый хранит только LDAP username/password в Keychain и всегда оставляет OTP ручным. Второй сохраняет старую рабочую конфигурацию с Bitwarden, Touch ID, permanent 2FA secret, hidden browser, autofill и submit.
Target-Perspective Output: Пользователь переустанавливает пакет, выбирает один из двух режимов, видит тот же выбор в vpn --status и в LemanaVPN.app. В ручном режиме он может подключиться с подстановкой логина/пароля, каждый раз вводит OTP руками и может обновить логин/пароль из меню. В Bitwarden-режиме работает прежний автоматический путь.
Truth Owner: Два repo profile-template файла владеют режимами; install.sh выбирает один профиль и пишет активный ~/.config/lemana-vpn/env; bin/vpn-lemanapro.sh исполняет режим; Swift app только читает CLI JSON и запускает CLI.
Contract Boundary: Public contract: mode id manual-keychain|bitwarden-auto, CLI flags/status JSON/app menu labels. Internal compatibility may still map old flags to a mode, but UI/docs/installer no longer expose four profiles as first-class choices.
Cutover: Fresh install defaults to manual-keychain and offers bitwarden-auto as the old personal advanced mode. Existing env files with manual, saved-login, saved-totp, or auto are migrated or normalized to one of the two public modes during install/status.
Displaced Path: Displace public manual, saved-login, saved-totp, and auto profile matrix. Keep only minimal compatibility aliases where needed, and make them resolve to one of the two modes.
Value Density: The smallest high-value slice removes mode ambiguity and stale install drift while preserving the two flows the user actually needs.
Acceptance Evidence: Clean temp HOME install output shows only two modes. Source and installed vpn --status --json agree on mode/status JSON. Fake launch captures prove manual-keychain uses visible browser, login/password autofill, no TOTP autofill, no submit; bitwarden-auto uses Bitwarden sync, Touch ID when enabled, hidden browser, TOTP autofill, submit. Installed CLI shasum matches checkout and installed app binary contains the updated menu strings.
Evidence Lane: Non-live first: shell syntax, smoke tests, Swift build, git diff --check, source/installed status JSON and installed binary string checks. Live VPN check only after vpn --status, starting with manual-keychain visible flow; skip if connected/connecting unless explicitly approved.
Kill Criteria: No installer prompt, README section, app menu, or available_modes JSON may present more than two public modes. No fresh install may require Bitwarden. No manual-keychain path may save or require TOTP seed. No bitwarden-auto path may silently degrade into half-configured Keychain-only auto.
Architecture Slice: install.sh, bin/vpn-lemanapro.sh, two new profile-template files, Swift app menu contract, README, smoke tests, installed-path refresh checks.
Plan Review Gate: PRE self-review done against Krypton reviewer checklist; external subagent review not run because subagents require an explicit user request in this environment.
Короткий План
🎯 Цель: оставить ровно два рабочих режима установки и подключения: ручной с Keychain login/password + ручной OTP, и старый Bitwarden/Touch ID automatic flow.
📦 Репозитории: только /Users/dokril/git/home/lemana-vpn.
🧩 Модули: install.sh, bin/vpn-lemanapro.sh, app/Sources/LemanaVPN, templates/, tests/smoke.sh, README.md; installed refresh checks для ~/bin/vpn-lemanapro.sh и ~/Applications/LemanaVPN.app.
🛠 Основные изменения:
- Создать два profile-template файла в
templates/:manual-keychain.envиbitwarden-auto.env. - Перевести installer на выбор только этих двух режимов; убрать публичные вопросы про
saved-login/saved-totp/auto. - В CLI ввести public mode
LEMANA_VPN_MODE=manual-keychain|bitwarden-autoи нормализовать старые env/flags в один из двух режимов. - Сделать app menu и status JSON двухрежимными:
Подключить,Подключить старым автоматическим способом,Изменить логин и пароль. - Оставить старые flags/aliases только как compatibility shims, без отдельного третьего режима в UI/docs.
- Переписать smoke tests под два режима и добавить installed-path evidence:
shasum,strings,vpn --status --json. - Сжать README до двух сценариев установки и восстановления.
✅ Проверка: sh -n install.sh, sh -n uninstall.sh, bash -n bin/vpn-lemanapro.sh, sh -n tests/smoke.sh, tests/smoke.sh, swift build -c release --package-path app, git diff --check, затем installed checks после sh install.sh.
⚠️ Риски: старые env с saved-totp/auto могут мигрировать не туда; installed CLI/app могут снова отставать; live auto-flow зависит от Bitwarden/Touch ID и permanent 2FA secret.
➡️ Следующий шаг: начать с mode contract и двух template-файлов, затем подключить installer/runtime normalization.
Architecture Slice
Files to create:
templates/manual-keychain.env- source profile for the normal manual mode.templates/bitwarden-auto.env- source profile for the old personal automatic mode.
Files to modify:
install.sh- two-mode wizard, env rendering from templates, migration from old flags/env, final next steps.bin/vpn-lemanapro.sh- public mode parser, compatibility mapping, status JSON, app menu JSON, runtime env for autofill/click/TOTP.app/Sources/LemanaVPN/VPNManager.swift- two launch modes, menu labels, status decoding if JSON shape changes.app/Sources/LemanaVPN/LemanaVPNApp.swift- remove multi-mode rendering that exposes old profiles; keep credential edit action.tests/smoke.sh- replace four-profile assertions with two-mode assertions plus compatibility tests.README.md- rewrite mode docs around two supported configurations.docs/goals/manual-first-auth-flow/*- mark superseded or point to this plan to avoid executing the older four-profile plan.
Files to avoid:
libexec/lemana-vpn-dns-cleanup- unrelated to auth mode split.templates/openconnect-lite-config.tomlunless fake launch evidence shows separate openconnect-lite configs are necessary.- Direct installed
openconnect-litesource edits; keep using the existing runtime patch path.
Source of truth:
- Repo templates define the two modes.
- Active install writes exactly one selected mode into
~/.config/lemana-vpn/env. - Keychain stores LDAP password for
manual-keychain; Bitwarden remains the upstream source forbitwarden-autoand syncs into Keychain at runtime.
Read path:
install.shreads selected mode and profile template, writes env.bin/vpn-lemanapro.shreads env, normalizes legacy fields, checks required credentials for the selected mode, emits JSON/status, launchesopenconnect-lite.- Swift app reads only
vpn --status --jsonand passes a CLI argument for the chosen mode.
Write path:
vpn --configure-loginwrites or updates username/password only formanual-keychain.- Bitwarden path reads item
LM LDAP, unlocks through Touch ID/manual master password, then writes password and normalized permanent TOTP secret into Keychain forbitwarden-auto. - Installer never writes the current 6-digit OTP.
Contract boundary:
- Public mode ids:
manual-keychain,bitwarden-auto. - Public status JSON should expose
mode.id,mode.label,mode.ready,mode.setup_hint, and a maximum of two user actions. - Compatibility inputs allowed:
--saved-login,--manual-full,--manual,--auto, oldLEMANA_VPN_AUTH_PROFILE, oldLEMANA_VPN_CREDENTIAL_SOURCE.
Integration points:
- macOS Keychain service
openconnect-lite. - Optional Bitwarden CLI item
LM LDAP. - Optional Touch ID helper
~/bin/keychain-fingerprint. templates/openconnect-lite-config.tomland runtime autofill patches.- Swift app JSON contract and installed app binary refresh.
Migration/cutover:
saved-login->manual-keychain.manual/manual-full->manual-keychain, but if no saved password exists, status shows setup hint and connection can still offer full manual fallback as diagnostic.saved-totp/auto+credential_source=bitwarden->bitwarden-auto.saved-totp/auto+credential_source=keychainis no longer a public mode; installer should ask to switch to eithermanual-keychainorbitwarden-auto, or keep a hidden compatibility shim for one release with a warning.
Displaced path:
- The old four-profile matrix.
- App
available_modesrendering multiple profile buttons. - README/installer wording that teaches
saved-login,saved-totp, andautoas equal modes.
Acceptance evidence gate:
- Fresh install dry-run contains only the two human mode names.
tests/smoke.shproves both launch profiles and legacy compatibility.~/bin/vpn-lemanapro.sh --status --jsonmatches source behavior aftersh install.sh.strings ~/Applications/LemanaVPN.app/Contents/MacOS/LemanaVPNproves updated menu labels are installed.
Tasks
Task 1 - Define Two Mode Templates
Scope: create templates/manual-keychain.env and templates/bitwarden-auto.env; add a tiny renderer/loader in install.sh.
Expected output: the selected profile writes one active public mode and its derived legacy compatibility vars.
Verification: sh install.sh --dry-run --non-interactive --mode manual-keychain and --mode bitwarden-auto show the correct env writes without exposing old profile names.
Acceptance evidence: dry-run output and rendered temp env contents.
Parallel: no; this is the source-of-truth base.
Task 2 - Normalize Runtime Modes
Scope: update bin/vpn-lemanapro.sh to parse LEMANA_VPN_MODE, map old env/flags to the two public modes, and derive autofill/TOTP/submit settings.
Expected output: manual-keychain uses visible browser, password autofill, TOTP off, submit off; bitwarden-auto uses Bitwarden sync, TOTP on, hidden browser, submit on.
Verification: fake openconnect-lite capture in tests/smoke.sh; bash -n bin/vpn-lemanapro.sh.
Acceptance evidence: JSON events and capture files for both modes.
Parallel: after Task 1 only.
Task 3 - Simplify Installer And CLI UX
Scope: installer questions, flags, usage, aliases, final next steps, and legacy migration warning.
Expected output: installer asks one question with two choices, configures login/password for manual-keychain, and reserves Bitwarden/Touch ID setup for bitwarden-auto.
Verification: expect-based interactive smoke and dry-run smoke.
Acceptance evidence: captured installer transcript with no public saved-login/saved-totp/auto wording.
Parallel: can run after Task 1 while Task 2 is in progress, but integration proof must wait.
Task 4 - Update Swift Menu Contract
Scope: VPNManager.swift and LemanaVPNApp.swift.
Expected output: disconnected menu is two-mode aware and does not render old profile buttons. Credential edit remains a Terminal launcher for vpn --configure-login.
Verification: Swift source smoke, swift build -c release --package-path app, installed binary strings after install.
Acceptance evidence: menu string checks and build output.
Parallel: can run after Task 2 JSON shape is decided.
Task 5 - Rewrite Docs And Supersede Old Plan
Scope: README.md, docs/goals/manual-first-auth-flow/PLAN.md, docs/goals/manual-first-auth-flow/GOAL.md.
Expected output: docs teach only two modes and point the older four-profile plan to this plan.
Verification: rg -n "saved-totp|auth_profile=|credential_source=|four-profile|manual-full" README.md docs/goals.
Acceptance evidence: only compatibility sections mention old names.
Parallel: can run after Tasks 1-3 settle final names.
Task 6 - Final Verification And Installed Refresh
Scope: run non-live ladder, install locally, compare installed CLI/app, and optionally perform a live manual check if status is safe.
Expected output: checkout and installed paths agree; user can run the chosen mode from Terminal and app.
Verification: sh -n install.sh, sh -n uninstall.sh, bash -n bin/vpn-lemanapro.sh, sh -n tests/smoke.sh, tests/smoke.sh, swift build -c release --package-path app, git diff --check, sh install.sh, vpn --status --json, shasum bin/vpn-lemanapro.sh ~/bin/vpn-lemanapro.sh, installed app strings.
Acceptance evidence: command outputs plus a note if live VPN was skipped because status was connected/connecting.
Parallel: no; final integration gate.
PRE Self-Review
Mode: PRE
Verdict: aligned
Findings:
- minor: "two files" could mean runtime shell modules rather than env templates. Smallest correction: execution should confirm with the user before choosing a different file split, but the current plan makes templates the truth owner because the stated pain is reinstall/config drift.
- minor: legacy
saved-totp+ keychain-only users are not part of the requested two modes. Smallest correction: keep one-release compatibility warning or migration prompt rather than deleting the path blindly.
Recommended next gate: execute Task 1 and Task 2 together as the contract slice, then run smoke before touching Swift/docs.