codexport 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/prd.md ADDED
@@ -0,0 +1,434 @@
1
+ # Codexport PRD
2
+
3
+ Status: implemented as an initial Node.js CLI in this repository.
4
+
5
+ ## Goal
6
+
7
+ Build a low-friction way to replicate the useful parts of a master machine's
8
+ Codex setup to follower machines.
9
+
10
+ The main target is `~/.codex`. Machine1 is the source of truth. Follower
11
+ machines should stay up to date automatically while still being allowed to keep
12
+ machine-local additions such as local MCPs, local skills, local trust entries,
13
+ and local path overrides.
14
+
15
+ ## Product Principles
16
+
17
+ - Machine1 owns the canonical Codex configuration.
18
+ - Followers are read-only consumers of canonical state.
19
+ - Followers can keep local-only overlays unless those overlays explicitly
20
+ conflict with canonical names.
21
+ - Sync should feel zero-friction after first setup.
22
+ - Secrets and auth are part of the desired user experience, but they should not
23
+ be stored as plaintext in a GitHub repository.
24
+ - GitHub can store tool code and non-secret portable config. Tailscale should be
25
+ used for private master-to-follower transfer of secret-bearing bundles.
26
+ - Active Codex sessions should not be interrupted by config updates.
27
+
28
+ ## Current Decisions
29
+
30
+ ### Repository Location
31
+
32
+ The project will live at:
33
+
34
+ ```text
35
+ /home/ubuntu/workspaces/codexport
36
+ ```
37
+
38
+ ### Authority Model
39
+
40
+ Machine1 is canonical. Follower machines do not push changes back by default.
41
+
42
+ Follower-specific additions are allowed, but they remain local unless promoted
43
+ from Machine1 intentionally.
44
+
45
+ ### Sync Transport
46
+
47
+ Use a Tailscale-compatible pull model:
48
+
49
+ ```text
50
+ Machine1:
51
+ codexport serve
52
+
53
+ Follower:
54
+ codexport join
55
+ > Master Tailscale IP/name: machine1.tailnet.ts.net
56
+ ```
57
+
58
+ Followers pull from Machine1. Machine1 does not need to track and push to every
59
+ follower.
60
+
61
+ Tailscale reachability is sufficient for follower enrollment. The first version
62
+ does not require a separate one-time pairing code.
63
+
64
+ Machine1 should run the master server persistently as a user-level service.
65
+ Followers should be able to reconnect and sync whenever Machine1 is online.
66
+
67
+ On first join, the follower trusts the provided Tailscale address and stores the
68
+ master instance fingerprint. Later syncs must verify that fingerprint. If the
69
+ fingerprint changes, sync should refuse by default and require an explicit
70
+ re-enroll or trust-reset command.
71
+
72
+ ### One-Click Join
73
+
74
+ Machine1 should be able to generate a durable join artifact for followers.
75
+
76
+ Preferred UX:
77
+
78
+ ```text
79
+ codexport master link
80
+ ```
81
+
82
+ Outputs a permanent join link or command that a follower can run without
83
+ manually typing the master address:
84
+
85
+ ```text
86
+ npx codexport follower join "codexport://join?host=machine1.tailnet.ts.net&port=17342&fingerprint=..."
87
+ ```
88
+
89
+ If custom URL handling is too complex for the first implementation, the
90
+ fallback is a one-time copy-paste command:
91
+
92
+ ```text
93
+ npx codexport follower join --master http://machine1.tailnet.ts.net:17342 --fingerprint ...
94
+ ```
95
+
96
+ The join link should not include plaintext Codex secrets. It should include only
97
+ connection and trust bootstrap information:
98
+
99
+ - master host or Tailscale IP/name
100
+ - port
101
+ - master fingerprint
102
+ - optional protocol/version metadata
103
+
104
+ Because this is a permanent link, rotating the master fingerprint or changing
105
+ the master address should invalidate or require regenerating the link.
106
+
107
+ ### Platform Support
108
+
109
+ Linux and Windows 10/11 are first-class targets.
110
+
111
+ The design must avoid Linux-only assumptions in canonical state. Platform
112
+ differences should be handled through explicit path variables, platform-specific
113
+ service installers, and local overlays.
114
+
115
+ Initial platform requirements:
116
+
117
+ - Linux: support systemd user services when available.
118
+ - Windows 10/11: support a user-level scheduled task or equivalent per-user
119
+ background launcher.
120
+ - Both platforms: support a non-service fallback where `codexport sync` can
121
+ be run manually.
122
+ - Both platforms: preserve local-only overlays and refuse accidental conflicts
123
+ with canonical names.
124
+
125
+ ### Automatic Updates
126
+
127
+ Followers should sync automatically through a follower-only Codex `SessionStart`
128
+ hook:
129
+
130
+ - The hook runs a short best-effort sync before a new Codex session starts.
131
+ - If Machine1 is reachable and the content hash changed, the follower applies
132
+ the update before the session continues.
133
+ - If Machine1 is unavailable, the hook exits cleanly and Codex starts with the
134
+ most recently applied config.
135
+
136
+ This means followers are guaranteed to refresh at the Codex session boundary,
137
+ but they do not continuously sync while idle. Manual `codexport sync` remains
138
+ available when an immediate refresh is needed outside session startup.
139
+
140
+ ### Config Layering
141
+
142
+ The tool should generate the final `~/.codex/config.toml` from layers:
143
+
144
+ ```text
145
+ canonical config from Machine1
146
+ local follower overlay
147
+ generated final ~/.codex/config.toml
148
+ ```
149
+
150
+ The generated file should be backed up before replacement.
151
+
152
+ Local overlays are never synced back unless explicitly promoted.
153
+
154
+ ### MCPs
155
+
156
+ Master MCP definitions are canonical and synced by default.
157
+
158
+ Because MCP definitions may contain machine-specific paths and secrets, the tool
159
+ should make them portable through:
160
+
161
+ - path variables such as home and workspace root
162
+ - secret transfer over the Tailscale bundle
163
+ - local overlays for follower-specific additions
164
+
165
+ Canonical MCP names are reserved. Local MCPs may add new names. A same-name
166
+ local MCP should fail by default unless an explicit override is configured.
167
+
168
+ ### Skills
169
+
170
+ Master skills are canonical and synced by default.
171
+
172
+ Follower-local skills are allowed under non-conflicting names. Same-name local
173
+ skills should fail by default unless an explicit override is configured.
174
+
175
+ ### Include By Default
176
+
177
+ Portable or canonical Codex material:
178
+
179
+ - `AGENTS.md`
180
+ - `RTK.md`
181
+ - `config.toml` canonical sections
182
+ - `hooks.json`
183
+ - selected `hooks/`
184
+ - `prompts/`
185
+ - `rules/`
186
+ - `skills/`
187
+ - `skill-libraries/`
188
+ - MCP definitions
189
+ - tool manifest, likely `mise.toml`
190
+
191
+ ### Exclude By Default
192
+
193
+ Runtime debris and machine-local state:
194
+
195
+ - logs
196
+ - caches
197
+ - tmp directories
198
+ - shell snapshots
199
+ - compact handoffs
200
+ - SQLite runtime state
201
+ - sessions
202
+ - history
203
+ - local trust entries unless intentionally canonical
204
+ - follower-local overlays
205
+
206
+ Sessions, history, and SQLite runtime state are out of scope for v1. The first
207
+ version should sync config, auth, MCPs, skills, hooks, prompts, rules, and other
208
+ portable Codex material, but not live conversation/runtime databases.
209
+
210
+ ### Secret Handling
211
+
212
+ The user wants auth and important state to sync for a one-click experience.
213
+
214
+ The current design should not store plaintext secrets in GitHub. Instead:
215
+
216
+ - Machine1 can include secret-bearing files in a private export bundle served
217
+ over Tailscale.
218
+ - Followers fetch and apply that bundle during `join` or automatic sync.
219
+ - GitHub stores code and non-secret portable config, not plaintext token blobs.
220
+
221
+ Secret-bearing examples from current `~/.codex` include:
222
+
223
+ - `auth.json`
224
+ - `.credentials.json`
225
+ - `multi-auth/**`
226
+ - MCP environment values and command args that contain tokens/passwords
227
+
228
+ Secret-bearing sync should still exclude sessions, history, logs, and SQLite
229
+ runtime state in v1.
230
+
231
+ ## Proposed CLI Shape
232
+
233
+ This is a planning sketch, not an approved interface.
234
+
235
+ ```text
236
+ codexport master init
237
+ codexport master serve
238
+ codexport master link
239
+ codexport follower join
240
+ codexport sync
241
+ codexport apply
242
+ codexport hook install
243
+ codexport status
244
+ codexport master rebuild
245
+ ```
246
+
247
+ ## Distribution
248
+
249
+ The tool should be publishable as an npm package and runnable with `npx`:
250
+
251
+ ```text
252
+ npx codexport
253
+ ```
254
+
255
+ That means the runtime target is Node.js. Bun may be used for local development
256
+ and testing, but followers should not need Bun preinstalled just to run the
257
+ tool.
258
+
259
+ Recommended package shape:
260
+
261
+ - TypeScript source.
262
+ - Node-compatible CLI entrypoint declared through `package.json` `bin`.
263
+ - Published npm package with compiled JavaScript in `dist/`.
264
+ - `npx codexport follower join` as the lowest-friction first-run path.
265
+ - Optional later installer that writes the master service and follower hook.
266
+
267
+ The tool may still install or configure Bun as part of the synced development
268
+ toolchain if Machine1's `mise.toml` requests it.
269
+
270
+ The published package should require Node.js 20 or newer.
271
+
272
+ ## Implementation Language
273
+
274
+ Codexport should be implemented in TypeScript targeting Node.js 20 or newer.
275
+
276
+ Reasons:
277
+
278
+ - `npx codexport ...` is a core product requirement, so Node.js is the natural
279
+ runtime.
280
+ - TypeScript provides useful type safety for config layering, bundle manifests,
281
+ join-link parsing, platform branching, and path rewriting.
282
+ - Node.js has first-class enough support for Linux and Windows filesystem,
283
+ process, HTTP, and path APIs.
284
+ - The npm ecosystem covers the needed libraries for CLI parsing, TOML parsing,
285
+ file watching, archive handling, and tests.
286
+ - Requiring Bun on follower machines would work against the one-click goal.
287
+
288
+ Recommended implementation stack:
289
+
290
+ - TypeScript source.
291
+ - Node.js 20+ runtime.
292
+ - `package.json` `bin` entrypoint named `codexport`.
293
+ - `commander` or `cac` for CLI parsing.
294
+ - `smol-toml` or `@iarna/toml` for TOML parsing and writing.
295
+ - `chokidar` for master-side file watching.
296
+ - Vitest for tests.
297
+ - `tsup` or `tsx` plus `tsc` for builds.
298
+
299
+ Rejected for v1:
300
+
301
+ - Bun as required runtime: good local dev tool, but not appropriate as a
302
+ follower prerequisite.
303
+ - Go or Rust as the main implementation: good for single binaries, but weaker
304
+ for the required npm/npx distribution path and higher release complexity.
305
+ - Native platform helpers: defer until a specific Windows or Linux operation
306
+ cannot be implemented reliably from Node.js.
307
+
308
+ Expected roles:
309
+
310
+ - `master init`: create Machine1 canonical state.
311
+ - `master serve`: serve canonical bundle over Tailscale-reachable HTTP.
312
+ - `master link`: print a durable follower join link or copy-paste command.
313
+ - `follower join`: enroll a follower by asking for Machine1 Tailscale IP/name.
314
+ - `sync`: fetch and stage/apply updates.
315
+ - `apply`: apply already available canonical state plus local overlays.
316
+ - `hook install`: install follower-only Codex SessionStart sync hook.
317
+ - `status`: report role, master address, last revision, pending update, and local
318
+ conflicts.
319
+ - `master rebuild`: force rebuild the master bundle for repair/debugging.
320
+
321
+ ## Services
322
+
323
+ The tool should install a user-level master background service and follower
324
+ Codex hook rather than requiring administrator/root installation for normal
325
+ operation.
326
+
327
+ On Linux, user-level services should use systemd user units when available.
328
+
329
+ On Windows 10/11, the master service should use a per-user Scheduled Task or an
330
+ equivalent user-level background launcher. Windows followers should install only
331
+ the Codex SessionStart hook in v1.
332
+
333
+ Master service:
334
+
335
+ - Runs `codexport master serve`.
336
+ - Binds to `0.0.0.0` by default so followers can connect through the easiest
337
+ Tailscale-routable address.
338
+ - Uses port `17342` by default.
339
+ - Should enforce a configured allowlist or reject non-Tailscale peers where peer
340
+ detection is available.
341
+ - Publishes the current canonical revision and export bundle.
342
+ - Computes the canonical revision as a content hash over the selected export
343
+ files, normalized metadata, and relevant generated config.
344
+ - Watches selected `~/.codex` paths, debounces changes, rebuilds the bundle
345
+ automatically, and publishes the new revision without requiring manual action.
346
+ - Does not require a Codex session to be active.
347
+
348
+ Follower hook:
349
+
350
+ - Runs at Codex `SessionStart`.
351
+ - Checks the master revision.
352
+ - Downloads and validates new revisions when changed.
353
+ - Verifies the stored master fingerprint before accepting an update.
354
+ - Applies before the new Codex session proceeds.
355
+ - Uses a short timeout so Codex startup is not blocked for long when Machine1 is
356
+ offline.
357
+ - Does not require a follower background service in v1.
358
+
359
+ ## Local Overlay Layout
360
+
361
+ Recommended local-only state lives outside `~/.codex`:
362
+
363
+ ```text
364
+ ~/.codexport/local.toml
365
+ ~/.codexport/mcps.local.toml
366
+ ~/.codexport/skills/
367
+ ~/.codexport/overrides/
368
+ ```
369
+
370
+ Default meanings:
371
+
372
+ - `local.toml`: follower role, master URL, pinned fingerprint, polling interval,
373
+ path variables, and explicit override permissions.
374
+ - `mcps.local.toml`: follower-only MCP definitions.
375
+ - `skills/`: follower-only skills.
376
+ - `overrides/`: explicit same-name overrides for canonical MCPs or skills.
377
+
378
+ Canonical names win by default. A same-name local MCP or skill fails unless
379
+ `local.toml` explicitly allows that override.
380
+
381
+ ## Open Questions
382
+
383
+ 1. Should the master server require a one-time pairing code in addition to
384
+ Tailscale reachability? Decision: no, Tailscale reachability is enough.
385
+ 2. Should `codexport serve` be temporary per enrollment or a persistent user
386
+ service? Decision: persistent user service.
387
+ 3. How should active Codex sessions be detected reliably?
388
+ 4. Which files under `~/.codex` are canonical source files versus generated or
389
+ plugin-managed files?
390
+ 5. Should local overlays use TOML only, or should skills/MCP overrides be stored
391
+ as directory trees? Decision: TOML for config/MCP overlays, directories for
392
+ local skills and explicit overrides.
393
+ 6. Should followers use background polling services or only a Codex
394
+ SessionStart hook? Decision: only a follower Codex SessionStart hook in v1.
395
+ 7. How should Windows path variables map to Codex paths, especially when the
396
+ master is Linux and followers are Windows?
397
+ 8. What minimum Node.js version should the published npm package require?
398
+ Decision: Node.js 20 or newer.
399
+ 9. Should master serving use a fixed default port, and if yes, which one?
400
+ Decision: yes, port `17342`.
401
+ 10. Should followers pin the master fingerprint after first join?
402
+ Decision: yes, refuse changed fingerprints unless explicitly re-enrolled.
403
+ 11. Should master revisions use a content hash or monotonically increasing
404
+ number? Decision: content hash.
405
+ 12. Should master export rebuilds require a manual command or happen
406
+ automatically? Decision: master watches selected paths and rebuilds
407
+ automatically; manual rebuild exists only for repair/debugging.
408
+ 13. Should Machine1 generate a permanent follower join link or require manual
409
+ master address entry? Decision: generate a durable join link, with a
410
+ copy-paste command fallback if custom URL handling is not implemented in the
411
+ first version.
412
+ 14. Should v1 sync sessions, history, or SQLite runtime state? Decision: no.
413
+ 15. Should a SessionStart hook replace the follower background service?
414
+ Decision: yes for v1. Followers sync at Codex session startup, with manual
415
+ `codexport sync` available for immediate refreshes.
416
+
417
+ ## Known Risks
418
+
419
+ - Current `~/.codex/config.toml` contains hardcoded paths and sensitive values.
420
+ A literal copy would be brittle and risky.
421
+ - Codex may not support config includes, so generated config is probably needed.
422
+ - Syncing hooks is powerful and can affect every future Codex session.
423
+ - A hook-only follower design means followers do not refresh while idle unless
424
+ Codex starts or `codexport sync` is run manually.
425
+ - Private GitHub is not secret storage. Avoid plaintext secret commits.
426
+
427
+ ## Non-Goals For The First Implementation
428
+
429
+ - macOS support.
430
+ - GitHub-based plaintext secret sync.
431
+ - Push-based orchestration from Machine1 to followers.
432
+ - Mid-session mutation of active Codex behavior.
433
+ - Automatic promotion of follower-local changes.
434
+ - Syncing Codex sessions, history, logs, or SQLite runtime state.
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "codexport",
3
+ "version": "0.1.0",
4
+ "description": "sync a canonical Codex setup from one master machine to follower machines",
5
+ "author": "Microck <contact@micr.dev>",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "homepage": "https://github.com/Microck/codexport#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Microck/codexport.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Microck/codexport/issues"
15
+ },
16
+ "keywords": [
17
+ "codex",
18
+ "cli",
19
+ "sync",
20
+ "tailscale",
21
+ "mcp",
22
+ "skills",
23
+ "configuration",
24
+ "automation"
25
+ ],
26
+ "bin": {
27
+ "codexport": "./dist/index.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE",
33
+ "docs/prd.md"
34
+ ],
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit",
41
+ "test": "vitest run",
42
+ "dev": "tsx src/index.ts"
43
+ },
44
+ "dependencies": {
45
+ "chokidar": "4.0.3",
46
+ "commander": "14.0.2",
47
+ "smol-toml": "^1.6.1"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "24.10.1",
51
+ "tsx": "4.20.6",
52
+ "typescript": "5.9.3",
53
+ "vitest": "4.0.13"
54
+ }
55
+ }