diff --git a/library/qa/quality/2026-07-05-qa-report-prd-003-fleet-lifecycle.md b/library/qa/quality/2026-07-05-qa-report-prd-003-fleet-lifecycle.md new file mode 100644 index 0000000..98678f7 --- /dev/null +++ b/library/qa/quality/2026-07-05-qa-report-prd-003-fleet-lifecycle.md @@ -0,0 +1,136 @@ +# QA Report: PRD-003 Fleet Lifecycle (nectar scope) + +**Plan document:** `library/requirements/backlog/prd-003-fleet-lifecycle-login-and-uninstall/` (superproject; index + 003a + 003b) +**Audit date:** 2026-07-05 +**Base branch:** `main` (audit target: UNCOMMITTED changes) +**Head:** `feature/fleet-lifecycle` @ nectar (uncommitted working tree) +**Auditor:** quality-worker-bee +**Ordering:** `security-worker-bee` ran first and remediated in place (report: `library/qa/security/2026-07-04-security-audit-prd-003-fleet-lifecycle.md`). No ordering violation. +**Scope:** nectar-side ACs only: a-AC-1/2/3/5/6/7, b-AC-1..6, module AC-8/AC-9. AC ledger: `library/ledger/EXECUTION_LEDGER-fleet-lifecycle.md` (superproject). + +## Summary + +**PASS.** All fifteen nectar-scope acceptance criteria trace to implementation and named tests; the three security remediations (H1 atomic credentials write, M1 win32 `shell: true` npm probe, M2 https-validated printed URL plus terminal sanitization) regressed no AC, and the credentials writer's temp+rename rewrite provably preserves byte-compatibility with honeycomb's `DiskCredentials` shape. One Warning was found and remediated in place during this audit (the accepted "no hot-attach of brood/enricher on credential appearance" deviation was undocumented; a code comment now records it at the credentials-watch wiring). Gates after remediation: build clean, typecheck clean, 828 tests with 821 passing, 0 failing, 7 skipped (5 platform-conditional POSIX-mode skips on win32 plus the 2 documented environment-flaky live Deeplake probes, which self-skipped as "Deep Lake unreachable" this run; both files untouched by this branch and green in CI under `nectar_ci`). + +## Scorecard + +| Category | Status | Notes | +|---------------|--------|-------| +| Completeness | ✅ | All 15 nectar-scope ACs implemented with AC-named tests. | +| Correctness | ✅ | Security remediations verified non-regressive; gates green; login exit codes correct (0/1/2). | +| Alignment | ✅ | Bare verbs + aliases per orchestrator decision 4; three-part uninstall order matches the PRD's stop, unit, registry, state-dir sequence. | +| Gaps | ⚠️ | One documentation gap (undocumented accepted deviation), remediated in place during this audit. | +| Detrimental | ✅ | No regressions: existing verbs and handlers untouched; additive daemon options default to pre-003a behavior when unset. | + +## Critical Issues (must fix) + +None. + +## Warnings (should fix) + +- [x] **Accepted deviation "no hot-attach of brood/enricher on credential appearance" was undocumented**, `src/cli.ts:1345-1360` (pre-fix) + + The W1-N ledger entry (2026-07-04 22:56) records the accepted deviation: a no-credentials boot does not hot-attach the brood/enricher subsystems when credentials later appear; only `/health` flips and active projects reconcile. Nothing in the code or knowledge docs recorded this, so a maintainer reading the credentials-watch wiring would reasonably assume login-after-boot fully activates the daemon. **REMEDIATED IN PLACE** (authorized by the audit dispatch): a concise `ACCEPTED DEVIATION` comment now sits at the `credentialsWatch` wiring in `runDaemon`, naming the dormant subsystems, the restart requirement, the honeycomb parity, and where a future hot-attach belongs (`daemon.ts` `onChange`). Comment-only change; build, typecheck, and the full suite re-ran green after it. + + ```ts + // ACCEPTED DEVIATION (W1-N, ledger 2026-07-04): credentials appearing after + // boot flip /health healthy and reconcile active projects, but the + // creds-dependent subsystems assembled above from this boot-time snapshot + // (the durable store, brood wiring, enricher loop) are NOT hot-attached; + ``` + +## Suggestions (consider improving) + +- [ ] **No leftover-temp-file assertion in the credentials-writer suite**, `test/credentials-writer.test.ts:22-58` + + The registry deregister suite asserts the atomic rename left no `.tmp` file (`test/doctor-registry-deregister.test.ts:38`); the credentials-writer suite (which covers the security H1 temp+rename rewrite) does not. The happy path cannot leave one (rename consumes it) and the failure path force-removes it, but a one-line `readdirSync(dir)` assertion would pin the contract the same way the registry test does. + +- [ ] **`nectar install` auto-login still runs when the service-unit install failed**, `src/cli.ts:335-366` + + `maybeAutoLoginOnInstall()` runs unconditionally after the registry write, so a machine where the unit install failed (`serviceResult.ok === false`, eventual exit 1) can still open a sign-in browser popup before the failure is reported. Best-effort login is by design and no AC forbids this, but gating the popup on `serviceResult.ok` would avoid a confusing popup-then-exit-1 sequence. + +- [ ] **Cross-repo note carried from the security wave (out of nectar scope):** nectar's M1 fix (`shell: true` on win32 for the constant-argv `npm.cmd` probe, `src/fleet-detection.ts:157-178`) intentionally diverges from honeycomb, whose audit left the identical dead probe as a documented Low (S3 silently absent on Windows there, failing toward SOLO, the popup direction). Ruling: honeycomb SHOULD adopt the same fix for contract parity on the fleet's primary platform; the mirrored fleet-detection contracts now behave differently on win32. This is a honeycomb-repo change and is deferred to that repo's next cycle; no nectar AC is affected. + +## Audit-directive verification (the four dispatched items) + +### 1. Security remediations did not regress any AC + +- **H1 (atomic credentials write), `src/hive-graph/deeplake-credentials.ts:753-778`.** Byte-compatibility with honeycomb's `DiskCredentials` survives the temp+rename rewrite: both writers serialize `JSON.stringify(record, null, 2) + "\n"`; nectar's written field set (`token`, `orgId`, `orgName`, `userName`, `workspaceId`, `apiUrl`, `savedAt`) is a strict subset of honeycomb's shape (`honeycomb/src/daemon/runtime/auth/credentials-store.ts:85-111`, where `agentId` and `tenancyConfirmedAt` are optional additive fields); `savedAt` is stamped server-side in both. The tests still prove it: `test/credentials-writer.test.ts:40-48` `deepEqual`s the exact on-disk JSON against the honeycomb shape AFTER the rename, `:60-69` asserts 0600 survives the rename on POSIX, `:71-93` asserts the fresh dir is 0700, and `test/device-flow-login.test.ts:94-99` round-trips a full login write through `loadDeepLakeCredentials`. The rename-replaces-inode property (a pre-existing loose-mode file never carries the new token) is documented in the writer's doc block. +- **M1 (win32 `shell: true` npm probe), `src/fleet-detection.ts:157-178`.** Scoped to win32 only; every argv element is a compile-time constant; non-Windows keeps `shell: false`. a-AC-6 classification tests (`test/fleet-detection.test.ts`) exercise the S3 seam and remain green; the fail direction (any error resolves `false`, absent signal) is unchanged. +- **M2 (printed URL validation + terminal sanitization), `src/auth/device-flow.ts:289-311, 499-503, 218`.** The printed `verification_uri` now refuses non-https with a clear `{ ok: false }` message, which conforms to AC-9 (terminate with an actionable error) rather than regressing a-AC-7; the a-AC-7 ordering tests (print before open, opener failure tolerated, non-https complete URL never opened, poll exhaustion bounded at `DEFAULT_MAX_POLLS`) all pass. + +### 2. The no-hot-attach deviation is now documented + +Found undocumented (repo-wide grep for hot-attach/reattach/subsystem-on-credentials found nothing); remediated as the Warning above. + +### 3. Regression sweep + +The `main()` dispatch (`src/cli.ts:1476-1558`) keeps every pre-existing verb routed to its unchanged handler: `daemon`, `install`, `uninstall` (handler upgraded per 003b, see traceability), `service-status` (now an alias of the new `status`, same `runServiceStatus` handler), `rebuild-projection`, `brood`, `prune`, `review-matches`, `search`, `projects`, `brooding`, `project --rebuild-projection`. New verbs: `login`, `start`, `stop`, `status`. The USAGE banner (`src/cli.ts:112-149`) documents every verb that dispatches and nothing that does not; `test/cli-verbs.test.ts:32-50` proves `--help` lists the lifecycle verbs and exits 0. The daemon assembly changes are additive and opt-in: `storageCredentialsPresent` undefined leaves the legacy `ok` posture (`src/daemon.ts:539-550`), `credentialsWatch` undefined arms nothing (`src/daemon.ts:622-649`), and the full pre-existing suite (818 pre-branch tests) passes unmodified; no existing test file needed edits for the `ServiceUninstallResult` contract change because no pre-existing test consumed `ServiceModule.uninstall` directly. + +### 4. Login UX coherence (AC-3 / AC-9) + +- Success: `Signed in as . Using org (), workspace .` then `nectar login: signed in; credentials written to .`, exit 0 (`src/cli.ts:394-401`, `src/auth/device-flow.ts:551-558`). +- Failures are plain-language and actionable, exit 1: `sign-in timed out before it was approved; run 'nectar login' again.`; `the sign-in code expired; run 'nectar login' again`; `sign-in was denied`; multi-org refusal names `--org=` and enumerates the available orgs; the non-https refusal names the cause. +- Flag misuse exits 2 with `nectar login: --org requires a value` / `unknown flag ''` on stderr (`src/cli.ts:359-365, 388-393`). +- Bounded termination: poll cap 900, retry budget 3, slow_down growth; `test/device-flow-login.test.ts:307-349` proves exhaustion terminates with the actionable message and writes no credentials. + +## Plan Item Traceability + +| # | Plan Requirement | Status | Implementation Location | Tests / Notes | +|---|---|---|---|---| +| a-AC-1 | Fleet install: no popup/prompt; daemon serves 503 degraded (storage unreachable) on /health | ✅ | `src/health.ts:159-173` (`setStorageState`), `src/daemon.ts:539-550`, `src/cli.ts:1350` | `test/fleet-lifecycle-health.test.ts:11-34`, socket-level 503 + machine-readable reason `test/fleet-lifecycle-daemon.test.ts:68-113` | +| a-AC-2 | Credentials appearing flips /health healthy without restart | ✅ | `src/credentials-watch.ts:46-88`, `src/daemon.ts:622-649, 1027-1029` | `test/fleet-lifecycle-daemon.test.ts:135-171` (same running daemon, manual timer); change-only semantics `test/fleet-lifecycle-health.test.ts:50-80`. Accepted deviation (health-only recovery, no brood/enricher hot-attach) now documented at `src/cli.ts:1351-1358` | +| a-AC-3 | Solo + no credentials: install auto-opens the popup; credentials present: no popup | ✅ | `decideInstallLoginAction` + `maybeAutoLoginOnInstall`, `src/cli.ts:377-418, 358` | `test/install-login-decision.test.ts:10-21`; best-effort (never fails the install) per the a-AC-3 note in the PRD ("auto-popup fires from the install path, not from every daemon boot"), honored: daemon boot never pops | +| a-AC-5 | `nectar login` exists, opens the popup in both modes, credentials readable on next storage attempt | ✅ | `src/auth/device-flow.ts:475-568`, `src/cli.ts:387-401` | `test/device-flow-login.test.ts:71-105` (round-trip through `loadDeepLakeCredentials`), tenancy explicitness `:198-252` | +| a-AC-6 | Deterministic solo/fleet classification; fired signals visible | ✅ | `src/fleet-detection.ts:187-207` (any signal = fleet; `fleetSignalLine`), logged at install `src/cli.ts:424` | `test/fleet-detection.test.ts:23-118` (all signal combinations + S1 file reads) | +| a-AC-7 | Headless: URL + code printed, polls to completion, never hangs or crashes | ✅ | `src/auth/device-flow.ts:494-527` (print before open, bounded poll), opener `:320-335` | `test/device-flow-login.test.ts:107-196, 254-349` (ordering, open failure, non-https refusal, slow_down growth, poll-cap exhaustion) | +| b-AC-1 | `start` and `stop` on macOS, Linux, Windows | ✅ | `src/service/argv.ts:143-190` (per-manager argv incl. `sc`), `src/service/index.ts:392-432`, `src/lifecycle.ts:62-117`, CLI `src/cli.ts:203-224` | `test/service-start-stop.test.ts:48-87`, `test/lifecycle.test.ts:64-157`. Fixed-argv tested, not live-smoked (same posture the W2 verifier accepted for honeycomb) | +| b-AC-2 | `uninstall` removes the OS unit (current + best-effort legacy) so it no longer boots | ✅ | `src/service/index.ts:344-390` (classified `ServiceUninstallResult`), `deregisterLegacy` `:434-458`, hard-failure surfacing `src/lifecycle.ts:190-205` | `test/lifecycle.test.ts:178-239` (genuine failure exits 1; already-absent stays exit 0), `test/service-start-stop.test.ts:89-103` | +| b-AC-3 | `uninstall` deletes nectar's doctor registry entry, other entries intact | ✅ | `src/doctor-registry.ts:520-608` (`deregisterFromDoctor`, fleet + legacy candidates, atomic rewrite) | `test/doctor-registry-deregister.test.ts:18-104` (preservation, malformed-file report, both candidate files, no temp residue) | +| b-AC-4 | `uninstall` removes only nectar's state dir; no registry wholesale, no `~/.deeplake` | ✅ | `src/lifecycle.ts:146-158` (`removeStateDir`: absolute-only, no symlink follow), wired to `nectarStateDir()` `src/cli.ts:270-283` | `test/lifecycle.test.ts:306-329` (non-absolute refused, symlink skipped) | +| b-AC-5 | Existing spellings keep working as aliases | ✅ | `src/cli.ts:1509-1512` (`status` \|\| `service-status` to the same handler); `daemon`/`install`/`uninstall` spellings unchanged | `test/cli-verbs.test.ts:15-30`. Nectar's pre-003 verb set was `daemon`/`install`/`uninstall`/`service-status`; all still dispatch | +| b-AC-6 | `uninstall` on a not-installed machine exits 0 "nothing to remove" | ✅ | `src/lifecycle.ts:239-253` (`anythingRemoved` gate), already-absent classification `src/service/index.ts:186-215` | `test/lifecycle.test.ts:161-174` | +| AC-8 (nectar scope) | No deletion outside the enumerated allow-list | ✅ | `removeStateDir` guards (absolute, no symlink, single resolved dir); registry rewrite preserves all other entries and unknown keys | `test/lifecycle.test.ts:318-329`, `test/doctor-registry-deregister.test.ts:18-42` | +| AC-9 (nectar scope) | Every flow terminates with a clear success or plain-language actionable error | ✅ | Bounded polls (`DEFAULT_MAX_POLLS`, `DEFAULT_MAX_RETRIES`), exit codes 0/1/2, per-step uninstall reporting, non-TTY multi-org refusal naming flags | `test/device-flow-login.test.ts:307-349`, `test/lifecycle.test.ts:110-121, 279-292`, `test/cli-verbs.test.ts:32-50` | +| NG (003a) | No change to device-flow protocol or credential file format | ✅ | The client ports honeycomb's wire shapes verbatim; the written file is a subset-compatible `DiskCredentials` | Verified under directive 1 above | +| NG (003b) | `uninstall` does not remove the npm package, `~/.deeplake`, or other products' dirs | ✅ | `src/lifecycle.ts:252` prints the package-left-in-place note; no `~/.deeplake` path in any removal | Orchestrator decision 5 honored | +| NG (003a) | Hive onboarding/login-step untouched; no gate reintroduced that fails on 503-degraded | ✅ | No hive files touched; nectar only serves the degraded posture, never probes it | Fleet 0.5.1 probe posture unmodified | + +## Remediations applied during this audit + +| # | Severity | Change | File | +|---|---|---|---| +| 1 | Warning | Documented the accepted no-hot-attach deviation with an `ACCEPTED DEVIATION` comment at the credentials-watch wiring (comment-only; gates re-run green) | `src/cli.ts:1351-1358` | + +## Baseline verification (after remediation) + +| Check | Result | +|---|---| +| `npm run build` | exit 0, clean | +| `npm run typecheck` | exit 0, clean | +| `npm test` | exit 0: 828 tests, 821 pass, 0 fail, 7 skipped. Skips: 4 POSIX-mode assertions on win32, 1 POSIX-only permission warning test, and the 2 documented environment-flaky live Deeplake probes (`hive-graph-deeplake` round-trip, 018h vector ordering), which self-skipped as "Deep Lake unreachable" this run; both files untouched by this branch and green in CI under `nectar_ci` | + +## Files Changed + +- `library/qa/security/2026-07-04-security-audit-prd-003-fleet-lifecycle.md` (A), the security wave's audit record (input to this report, not audited content) +- `src/auth/device-flow.ts` (A), in-process zero-dependency Deeplake device-flow client: code request, bounded poll, explicit tenancy, mint, `/me`, credential persist (a-AC-5/7) +- `src/cli.ts` (M), `login`/`start`/`stop`/`status` verbs, install auto-popup decision, three-part uninstall wiring, daemon boot storage posture + credentials watch, USAGE update +- `src/credentials-watch.ts` (A), change-only poll watch that flips the /health storage posture without restart (a-AC-2) +- `src/daemon.ts` (M), additive `storageCredentialsPresent` + `credentialsWatch` assemble options; watch armed on start, drained on shutdown +- `src/doctor-registry.ts` (M), additive `deregisterFromDoctor` (fleet + legacy candidate files, atomic, best-effort per file) (b-AC-3) +- `src/fleet-detection.ts` (A), three-signal solo/fleet classifier with injectable seams and the win32 `shell: true` npm probe (a-AC-6) +- `src/health.ts` (M), additive `storage` health slice + `setStorageState` (the sole runtime `status` toggle) (a-AC-1/2) +- `src/hive-graph/deeplake-credentials.ts` (M), additive `DiskCredentials` shape + atomic `saveDeepLakeCredentials` writer (0700 dir, 0600 temp+rename) +- `src/lifecycle.ts` (A), start/stop/uninstall orchestration behind injectable seams; guarded `removeStateDir` (b-AC-1/2/3/4/6) +- `src/service/argv.ts` (M), additive per-manager `startCommands`/`stopCommands` (b-AC-1) +- `src/service/index.ts` (M), `ServiceModule.start`/`stop`/`deregisterLegacy` + classified `ServiceUninstallResult` with `isAlreadyAbsentFailure` (b-AC-1/2/6) +- `test/cli-verbs.test.ts` (A), verb dispatch + alias + USAGE coverage (b-AC-5, AC-9) +- `test/credentials-writer.test.ts` (A), on-disk shape byte-compatibility + POSIX modes (a-AC-5) +- `test/device-flow-login.test.ts` (A), login flow end-to-end through fake fetch: ordering, headless, tenancy, slow_down, poll exhaustion (a-AC-5/7, AC-9) +- `test/doctor-registry-deregister.test.ts` (A), registry delete preservation/atomicity/best-effort (b-AC-3) +- `test/fleet-detection.test.ts` (A), classification determinism + evidence line (a-AC-6) +- `test/fleet-lifecycle-daemon.test.ts` (A), socket-level 503 + no-restart recovery on a running daemon (a-AC-1/2) +- `test/fleet-lifecycle-health.test.ts` (A), health-state storage posture unit coverage (a-AC-1/2) +- `test/install-login-decision.test.ts` (A), install popup decision matrix + login flag parsing (a-AC-3/5) +- `test/lifecycle.test.ts` (A), start/stop/uninstall orchestration incl. hard-failure vs already-absent classification and state-dir guards (b-AC-1/2/4/6, AC-8/9) +- `test/service-start-stop.test.ts` (A), per-manager start/stop argv + legacy deregister (b-AC-1/2) diff --git a/library/qa/security/2026-07-04-security-audit-prd-003-fleet-lifecycle.md b/library/qa/security/2026-07-04-security-audit-prd-003-fleet-lifecycle.md new file mode 100644 index 0000000..260eea7 --- /dev/null +++ b/library/qa/security/2026-07-04-security-audit-prd-003-fleet-lifecycle.md @@ -0,0 +1,69 @@ +# Security Audit - PRD-003 (fleet lifecycle: device-flow login, credentials writer, lifecycle verbs) + +- **Date:** 2026-07-04 +- **Auditor:** security-worker-bee +- **Scope:** UNCOMMITTED changes on `feature/fleet-lifecycle` @ `C:/Users/mario/GitHub/the-apiary/nectar` (HEAD `a5b1b56`): the new in-process device-flow client `src/auth/device-flow.ts`, the credentials writer additions in `src/hive-graph/deeplake-credentials.ts`, `src/credentials-watch.ts`, `src/fleet-detection.ts`, `src/lifecycle.ts`, the doctor-registry deregister (`src/doctor-registry.ts`), service start/stop argv (`src/service/argv.ts`, `src/service/index.ts`), health storage posture (`src/health.ts`, `src/daemon.ts`), and the CLI login/install/start/stop/uninstall wiring (`src/cli.ts`), plus the ten new test suites. +- **Ordering:** ran BEFORE `quality-worker-bee`. The only quality report on disk (`library/qa/quality/2026-07-02-wave-cde-quality-closeout.md`) predates this branch's work and does not cover it; no ordering inversion. +- **Threat model:** same class as Hivemind - Deeplake credentials (`~/.deeplake/credentials.json`), device-flow auth against `api.deeplake.ai`, loopback daemon. This repo is zero-runtime-dependency TypeScript/Node, so the Deeplake SQL, MCP, and harness catalog items do not apply here; credential/token handling, subprocess hygiene, and filesystem containment items apply in full. + +--- + +## Executive summary + +**One High finding and two Medium findings, all remediated in-session. Four Low findings documented and accepted. No Critical findings. Gates green post-fix (the single full-suite failure is the known environment-flaky live Deeplake ordering probe, excluded from judgment).** + +## Findings + +| # | Severity | Location | Finding | Disposition | +|---|---|---|---|---| +| H1 | **High** | `src/hive-graph/deeplake-credentials.ts:216-228` (pre-fix) | The credentials writer used a direct `writeFileSync` on the SHARED `~/.deeplake/credentials.json`. Two consequences: (a) a crash mid-write leaves a truncated/partial token file that breaks BOTH nectar and honeycomb until re-login (and flaps nectar's own credentials watch, which polls this file); (b) `writeFileSync`'s `mode: 0o600` applies only at file CREATION, so a pre-existing group/other-readable file (hand-written, or laid down loose by another tool) would receive the freshly minted long-lived org-bound token while STAYING world-readable - credential exposure, never-downgrade class. | **Fixed**: atomic same-directory temp + rename. The temp file is created at `0600` from birth (no write-then-chmod window), the rename replaces the old inode so a loose-mode predecessor never carries the new token, and the token-bearing temp is force-removed on any failure so no partial file survives. Verified by `test/credentials-writer.test.ts` (mode + shape assertions still green). | +| M1 | Medium | `src/fleet-detection.ts:154-175` (pre-fix) | The S3 npm probe ran `execFile("npm.cmd", ..., { shell: false })` on win32. Since Node's CVE-2024-27980 hardening (all Node >= 20.12, and this repo requires >= 22), spawning a `.cmd` with `shell: false` throws EINVAL, so the probe's catch resolved `false` on EVERY Windows machine - silently blinding one of the three signals that suppress the auto sign-in popup, weakening the fleet-detection control on the product's primary platform. | **Fixed**: `shell: true` on win32 only. Safe here because every argv element is a compile-time constant (`ls -g @legioncodeinc/hive --depth 0`); no user input reaches the command line. Non-Windows platforms keep `shell: false`. | +| M2 | Medium | `src/auth/device-flow.ts:481` and `:218` (pre-fix) | Server-derived strings reached the terminal unvalidated: the PRINTED `verification_uri` skipped the https-only check (only the browser-open path validated), and `verification_uri`, `user_code`, and truncated HTTP error bodies were echoed raw, allowing ANSI/control-character terminal escape injection from a compromised or spoofed auth response. Printing a non-https URL also instructs the user to open it manually, sidestepping the opener's https guard. | **Fixed**: the printed URL now goes through `validateVerificationUrl` and the flow refuses (clear `{ ok: false }` message, no hang) on a non-https `verification_uri`; a new `sanitizeForTerminal` strips C0/C1 control characters (including ESC) from the printed user code and from the truncated error bodies carried in `AuthHttpError`. | +| L1 | Low | `src/hive-graph/deeplake-credentials.ts`, `src/doctor-registry.ts:217` | Predictable temp-file names (`pid` + timestamp) for the atomic writes. | Documented, accepted: consistent with the prior audit's accepted posture (2026-07-04 L4); the credentials dir is owner-only (`0700`) and the registry dir lives under the user-owned fleet root; rename is atomic. | +| L2 | Low | `src/service/index.ts` `isAlreadyAbsentFailure` | The generic case-insensitive text fallback (`does not exist|cannot find|no such process|not[- ]loaded|could not find|not found`) could misclassify a GENUINE uninstall failure whose stderr coincidentally contains one of those phrases (for example a "config file not found" sub-error) as already-absent, masking a boot-resurrecting unit while `nectar uninstall` reports a friendly no-op. The locale-independent numeric checks (sc 1060, launchd exit 3) are correct. | Documented, accepted for now: the misclassification window is narrow, the conservative default (anything unmatched is a genuine failure) points the right way, and a per-manager pre-flight status query would be the robust follow-up. | +| L3 | Low | `src/hive-graph/deeplake-credentials.ts` `saveDeepLakeCredentials` | The writer does not tighten a PRE-EXISTING loose-mode `~/.deeplake` DIRECTORY (`0700` applies only when nectar creates it), and `existsSync` follows a symlinked `~/.deeplake` into its target. The loader's `warnIfWorldReadable` advisory covers the file, not the dir. | Documented, accepted: the dir is shared with honeycomb/hivemind and chmod-ing it from nectar risks cross-tool surprise; a symlinked `~/.deeplake` requires home-directory write access, at which point the attacker already owns the trust boundary. | +| L4 | Low | `src/auth/device-flow.ts` `resolveTenancy` | An explicit `--org=` not present in the enumerated org list is accepted (`orgs.find(...) ?? { id: flags.org, name: flags.org }`) and passed to the mint. | Documented, accepted by design: the server is the authorization authority and refuses a mint for an org the account cannot access; the client-side list is UX, not an access control. | + +## Focus-area verification (checked clean) + +1. **Auth client token hygiene** (`src/auth/device-flow.ts`): + - The token rides ONLY in the `Authorization: Bearer` header (`authHeaders`); it never appears in a URL, log line, error message, or the `out` sink. `AuthHttpError` carries status + truncated server body only; the catch-all in `runDeviceFlowLogin` surfaces `err.message`, never response payloads that carry tokens (the only token-bearing responses are `resp.ok` paths that never feed an error). + - `device_code` is never printed; only `user_code` (by design, the user must type it) and the validated verification URL reach the terminal. + - Browser opener: fixed-argv `execFileSync`, `shell` never set (no shell), 5s timeout, `stdio: "ignore"`, https-only gate BEFORE any spawn; `open` (darwin) / `rundll32 url.dll,FileProtocolHandler` (win32) / `xdg-open` (else). Opener failure returns `false`, never crashes the flow (proven by `test/device-flow-login.test.ts` a-AC-7 cases). + - Poll floors honored: `Math.max(grant.interval || 5, 5) * 1000` floors at 5s, `slow_down` adds 5s and never shrinks, the poll count is hard-capped at `DEFAULT_MAX_POLLS` (900), and the retry budget for 429/5xx is bounded (3, exponential backoff). No hammering path exists. + - No token in any spawned process argv: the opener receives only the URL; the daemon direct-spawn (`src/cli.ts` `spawnDaemonDetached`) passes only `--experimental-sqlite