Replace Zod schemas with plain TypeScript validation in env.ts,
config.ts, and invite/parse.ts. Zod 4 classes break under bun
build --target=node (Class2 is not a constructor).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
End-to-end join: user runs `claudemesh join ic://join/<base64>` and
walks away with a signed member record + persistent keypair.
new modules:
- src/crypto/keypair.ts: libsodium ed25519 keypair generation. Format
is crypto_sign_keypair raw bytes, hex-encoded (32-byte pub, 64-byte
secret = seed || pub). Same format libsodium will need in Step 18
for sign/verify.
- src/invite/parse.ts: ic://join/<base64url(JSON)> parser with Zod
shape validation + expiry check. encodeInviteLink helper for tests.
- src/invite/enroll.ts: POST /join to broker, converts ws:// to http://
transparently.
rewritten join command wires them together:
1. parse invite → 2. generate keypair → 3. POST /join → 4. persist
config → 5. print success.
state/config.ts: saveConfig now chmods the file to 0600 after write,
since it holds ed25519 secret keys. No-op on Windows.
signature verification (step 18) + invite-token one-time-use tracking
are deferred. For now the invite link is a plain bearer token; any
client with the link can join.
verified end-to-end via apps/cli/scripts/join-roundtrip.ts:
build invite → run join subprocess → load new config → connect as
new member → send A→B → receive push. Flow passes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>