Skip to content

Commit 87d23b4

Browse files
NagyViktNagyVikt
andauthored
chore(release): v0.1.25 — Now horizon (N1, N2, N3, N4) + P0 wave (#31)
Bumps package.json + package-lock.json from 0.1.24 to 0.1.25 and extends releases/v0.1.25.md to cover every theme that landed since v0.1.24: - N1 durability (#29): atomic writes, registry lock, fsync-before-rename - N2 account-service split (#30): 1675 LOC -> 164 LOC orchestrator + 12 modules - N3 error taxonomy + --json (#28): AuthmuxError base, stable codes, JSON envelope - N4 lazy path resolvers (#27): bare constants deprecated for v0.2.0 - P0 wave (#26): secure perms, [y/N] update prompt, kiro ENOENT, CI matrix, registry proxy-source round-trip, 18k-line improvement docs Notes follow the canonical 7-section shape from docs/future/17-ROADMAP.md (Added, Changed, Fixed, Deprecated, Removed, Security, Migration), with the prior Durability section folded into a "Theme deep-dives" appendix so the canonical shape is intact. No code changes. No npm publish. Verification: - npm run build: clean - npm test: 129/129 pass - node -e "require('./package.json').version" -> 0.1.25 - node -e "require('./package-lock.json').version" -> 0.1.25 Co-authored-by: NagyVikt <nagy.viktordp@gmail.com>
1 parent 262ac9e commit 87d23b4

3 files changed

Lines changed: 169 additions & 39 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "authmux",
3-
"version": "0.1.24",
3+
"version": "0.1.25",
44
"description": "Multi-account auth multiplexer for AI CLI agents — Claude Code, Codex, Kiro CLI.",
55
"license": "MIT",
66
"bin": {

releases/v0.1.25.md

Lines changed: 166 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,132 @@
11
# authmux v0.1.25
22

3+
Now-horizon release: themes **N1** (durability), **N2** (account-service
4+
split), **N3** (error taxonomy + `--json`), **N4** (lazy path resolvers),
5+
plus the **P0 wave** from the 18k-line improvement protocol.
6+
37
## Added
4-
- `src/infra/fs/atomic-write.ts` — single durable file-write primitive with
5-
fsync-before-rename and POSIX dir-fsync after rename, plus mode applied
6-
before rename so 0600 files are never visible at a looser mode.
7-
- `src/infra/fs/registry-lock.ts` — advisory lock around `registry.json`
8-
with PID-liveness probing plus a 30 s wall-clock stale-lock heuristic.
9-
- `persistRegistryAtomic` in `src/lib/accounts/registry.ts` is now the single
10-
registry write path; it reloads under the lock and merges accounts so two
11-
concurrent writers (daemon + interactive) no longer race-lose each other's
12-
mutations.
13-
- `src/tests/registry-durability.test.ts` covers the SIGKILL-between-
14-
writeFile-and-rename window and the stale-lock-reaping path.
8+
9+
- **N1 — Atomic writes & registry lock.**
10+
- `src/infra/fs/atomic-write.ts` — single durable file-write primitive
11+
with `fsync`-before-rename, POSIX dir-fsync after rename, and mode
12+
applied before rename so 0600 files are never visible at a looser mode.
13+
- `src/infra/fs/registry-lock.ts``O_EXCL` advisory lock around
14+
`registry.json` with PID-liveness probe plus 30 s wall-clock stale-lock
15+
heuristic.
16+
- `persistRegistryAtomic` in `src/lib/accounts/registry.ts` — single
17+
registry write path; reloads under the lock and merges account entries
18+
so two concurrent writers no longer race-lose each other's mutations.
19+
- `src/tests/registry-durability.test.ts` — covers SIGKILL between
20+
`writeFile` and `rename` plus the stale-lock-reaping path.
21+
- **N2 — `account-service.ts` decomposition.** 1675 LOC → 164 LOC
22+
orchestrator backed by 12 focused modules under
23+
`src/lib/accounts/{sync,read,write,config,auto-switch,usage,safety,session,identity}/`
24+
plus 4 `_internal/` helpers. 31 new unit tests across the clusters.
25+
- **N3 — Error taxonomy.** `AuthmuxError` base class in
26+
`src/lib/accounts/errors.ts` with stable `code`, `severity`, `hint`,
27+
`details`, and `toJSON()`. Every existing error
28+
(`AuthFileMissingError`, `AccountNotFoundError`,
29+
`NoAccountsSavedError`, `InvalidAccountNameError`,
30+
`AccountNameInferenceError`, `SnapshotEmailMismatchError`,
31+
`PromptCancelledError`, `InvalidRemoveSelectionError`,
32+
`AmbiguousAccountQueryError`, `AutoSwitchConfigError`) now carries an
33+
`E_*` code. `src/tests/error-taxonomy.test.ts` enforces the §6.2 code
34+
allowlist and §6.3 exit-code table.
35+
- **N3 — `--json` flag** on `list`, `current`, `status`, `use`, `save`.
36+
Emits a single JSON envelope on stdout (`{ ok, data }` or
37+
`{ ok, error: { code, severity, message, hint, details } }`).
38+
Interactive prompts are skipped under `--json`.
39+
- **N4 — Lazy path resolver tests.** `src/tests/paths.test.ts` proves
40+
env-var changes apply after module load.
41+
- **P0 — CI test matrix.** New GitHub Actions workflow runs `npm test`
42+
across Ubuntu/macOS/Windows × Node 18/20/22 (previously only an LLM
43+
review bot existed; `npm test` was never run in CI).
44+
- **Docs.** `docs/future/*` — 19 source-grounded improvement documents
45+
(~18 016 lines) covering architecture, accounts, paths, config, daemon,
46+
usage, hooks, multi-CLI, security, testing, release, observability,
47+
cross-platform, perf, docs, roadmap, glossary.
1548

1649
## Changed
17-
- `secureWriteFile` now delegates to `atomicWriteFile`; behavior is the same
18-
(atomic temp+rename, 0600 perms) but every snapshot, `current`, and
19-
`sessions.json` write now also fsyncs the file and the containing directory
20-
before returning.
21-
- `AccountService.useAccount` no longer calls `saveRegistry` directly; all
22-
registry writes go through the locked `persistRegistry` path.
50+
51+
- **N1.** `secureWriteFile` now delegates to `atomicWriteFile`; every
52+
snapshot, `current`, and `sessions.json` write fsyncs the file and the
53+
containing directory before returning.
54+
- **N1.** `AccountService.useAccount` no longer calls `saveRegistry`
55+
directly; all registry writes route through the locked
56+
`persistRegistry` path.
57+
- **N2.** `AccountService` is now a thin orchestrator (164 LOC). All 21
58+
public method signatures preserved; the singleton in
59+
`src/lib/accounts/index.ts` is byte-compatible.
60+
- **N3.** `BaseCommand` central error handler routes `AuthmuxError`
61+
instances to the JSON envelope under `--json` and exits with the §6.3
62+
exit code (`3` `E_AUTH_MISSING`, `4` `E_ACCOUNT_NOT_FOUND`,
63+
`5` `E_SNAPSHOT_EMAIL_MISMATCH`, `6` `E_REGISTRY_LOCKED`,
64+
`7` `E_REGISTRY_CORRUPT`, `8` `E_PROVIDER_NOT_INSTALLED`,
65+
`64` `E_PROMPT_CANCELLED`, `1` generic).
66+
- **P0.** Init-hook update prompt flipped from `[Y/n]` (default-yes) to
67+
`[y/N]` (default-no) in `src/hooks/init/update-notifier.ts`. Bare
68+
`authmux` invocations no longer auto-install updates by default.
2369

2470
## Fixed
25-
- Half-written `registry.json` after SIGKILL or laptop sleep no longer
26-
shadows the on-disk file: the rename is the commit point.
27-
- Concurrent `authmux daemon --watch` + `authmux use foo` mutations are
28-
serialized through the registry lock.
71+
72+
- **N1.** Half-written `registry.json` after SIGKILL or laptop sleep no
73+
longer shadows the on-disk file; the rename is the commit point.
74+
- **N1.** Concurrent `authmux daemon --watch` + `authmux use <name>`
75+
mutations are serialized through the registry lock.
76+
- **P0.** `registry.ts` sanitization no longer drops `"proxy"` as a
77+
usage source; saved snapshots with `source: "proxy"` round-trip
78+
correctly instead of being coerced to `"cached"`.
79+
- **P0.** `src/commands/kiro.ts` no longer throws `ENOENT` when the
80+
target file does not exist (the `fs.existsSync(...) || fs.lstatSync(...).isSymbolicLink()`
81+
short-circuit was inverted).
2982

3083
## Deprecated
31-
- None.
84+
85+
- `codexDir`, `accountsDir`, `authPath`, `currentNamePath`,
86+
`registryPath`, `sessionMapPath` in `src/lib/config/paths.ts` — the bare
87+
eager constants. Use the `resolveX()` getters instead so env-var
88+
overrides set after import are honored. Scheduled for removal in
89+
**v0.2.0**.
3290

3391
## Removed
92+
3493
- None.
3594

3695
## Security
37-
- File mode 0600 is applied to the temp file BEFORE the rename, closing the
38-
brief window where the previous chmod-after-rename path could expose
39-
freshly-renamed files at the umask default.
4096

41-
## Durability
97+
- **0600 / 0700 perms.** `secureWriteFile` (now backed by
98+
`atomicWriteFile`) applies file mode 0600 to the temp file *before*
99+
the rename, closing the brief window where chmod-after-rename could
100+
expose freshly-renamed snapshot, `current`, or `sessions.json` files at
101+
the umask default. Accounts dir is created 0700.
102+
- **Atomic mode-before-rename** means there is no observable moment at
103+
which a refresh-token file exists on disk at a looser mode than 0600.
104+
- **Default-no update prompt.** Flipping the init-hook update prompt to
105+
`[y/N]` reduces the chance of accidental unattended updates triggered
106+
by a bare `authmux` invocation in CI or scripts.
107+
108+
## Migration
109+
110+
No migration is required for end users. The on-disk layout of
111+
`registry.json`, `current`, `sessions.json`, and per-account snapshot
112+
files is unchanged.
113+
114+
External library consumers of `src/lib/config/paths.ts` should switch
115+
from the bare constants (`codexDir`, `accountsDir`, `authPath`,
116+
`currentNamePath`, `registryPath`, `sessionMapPath`) to the resolver
117+
functions (`resolveCodexDir()`, `resolveAccountsDir()`, `resolveAuthPath()`,
118+
`resolveCurrentNamePath()`, `resolveRegistryPath()`,
119+
`resolveSessionMapPath()`) before **v0.2.0**.
120+
121+
CLI consumers that grep stdout still work — human-readable `message`
122+
strings are unchanged. New scripts should prefer `--json` for stable
123+
parsing.
124+
125+
---
126+
127+
## Theme deep-dives
128+
129+
### Durability (N1)
42130

43131
This release closes the two data-loss windows tracked under Theme N1 of
44132
`docs/future/17-ROADMAP.md`:
@@ -48,19 +136,61 @@ This release closes the two data-loss windows tracked under Theme N1 of
48136
and (on POSIX) `fsync`s the containing directory. A power loss between
49137
any of these steps leaves the previous content of the target intact.
50138
2. **Concurrent-writer lost mutations.** `withRegistryLock` serializes
51-
writers; `persistRegistryAtomic` reloads the registry under the lock and
52-
merges accounts before writing so neither writer silently discards the
53-
other.
139+
writers; `persistRegistryAtomic` reloads the registry under the lock
140+
and merges accounts before writing so neither writer silently discards
141+
the other.
54142

55-
Crash-safety guarantees promised after this release match the table in
143+
Crash-safety guarantees match the table in
56144
`docs/future/01-ARCHITECTURE.md` §5.3 rows 1, 2, and 4.
57145

58146
`fsync` adds latency on spinning disks; on SSDs it is dominated by the
59-
network/parse work the CLI was already doing and is not user-visible. The
60-
dir-fsync is gated on `process.platform !== "win32"` because Windows does
61-
not allow opening a directory as a file.
147+
network/parse work the CLI was already doing and is not user-visible.
148+
The dir-fsync is gated on `process.platform !== "win32"` because Windows
149+
does not allow opening a directory as a file.
62150

63-
## Migration
151+
### Account-service split (N2)
152+
153+
| Module | LOC | Cluster |
154+
| --- | --- | --- |
155+
| `sync/external-sync.ts` | 216 | external-auth sync orchestrator |
156+
| `read/listing.ts` | 211 | listings + `getCurrentAccountName` + find |
157+
| `write/save.ts` | 159 | `saveAccount` + safety guard + name inference |
158+
| `write/use.ts` | 128 | `useAccount` + `activateSnapshot` |
159+
| `write/remove.ts` | 105 | remove one / by-query / all |
160+
| `config/auto-switch-config.ts` | 82 | status + threshold setters |
161+
| `auto-switch/policy.ts` | 141 | `runAutoSwitchOnce` + `runDaemon` |
162+
| `usage/adapter.ts` | 192 | refresh + proxy shim |
163+
| `safety/snapshot-vault.ts` | 125 | backup vault + clobber recovery |
164+
| `session/pin.ts` | 243 | sessions.json I/O + Linux PPID heuristic |
165+
| `identity/equality.ts` | 86 | snapshot identity comparisons (pure) |
166+
| `naming.ts` | 40 | `normalizeAccountName` + `accountFilePath` |
167+
168+
Orchestrator `account-service.ts`: **164 LOC** (was 1675; ceiling was
169+
400 per N2 exit criteria).
170+
171+
### Error taxonomy & --json (N3)
172+
173+
JSON envelope shape:
174+
175+
```jsonc
176+
// success
177+
{ "ok": true, "data": { /* command-specific payload */ } }
178+
179+
// error
180+
{ "ok": false,
181+
"error": { "code": "E_ACCOUNT_NOT_FOUND", "severity": "fatal",
182+
"message": "...", "hint": "...",
183+
"details": { "name": "alice" } } }
184+
```
185+
186+
`CodexAuthError` is preserved as a back-compat subclass of
187+
`AuthmuxError` so existing `instanceof CodexAuthError` catches continue
188+
to work.
189+
190+
### Lazy path resolvers (N4)
64191

65-
No migration is required. The on-disk layout of `registry.json`, `current`,
66-
`sessions.json`, and the per-account snapshot files is unchanged.
192+
The bare exports in `src/lib/config/paths.ts` were evaluated at module
193+
import time, so env-var overrides set after the first `import` had no
194+
effect. The `resolveX()` getters re-read the environment on every call;
195+
all internal call sites already use them. The bare constants are kept
196+
through one release for external consumers.

0 commit comments

Comments
 (0)