Good, analysis saved. Here's the summary:
Answer: Secret Key Derivation
Yes, the secret is derived from the session ID — and this was your intentional change. In packages/sessions/src/index.tsx line 72:
const shareSecret = uuidv5(sessionID, c.env.SESSIONS_SHARED_SECRET)
It's a deterministic UUIDv5 using the sessionID as the name and your SESSIONS_SHARED_SECRET wrangler secret as the namespace. Upstream uses crypto.randomUUID() (fully random per share). Your change means you can recompute the secret locally from any sessionID + your shared secret, which is exactly what enables manual API calls without needing to look up stored credentials.
Why Sessions Aren't Sharing
There are three issues, in order of severity:
1. Likely blocker: Cloudflare WAF / Bot Fight Mode (silent failure)
Your AGENTS.md explicitly warns about this. If Bot Fight Mode is active on j9xym.com, the CLI's POST /api/share gets an HTML challenge page. The CLI does .then(x => x.json()) which throws, but there's zero error handling — the share record never gets stored locally, and every subsequent sync call silently bails via if (!share) return.
Quick test: curl -v -X POST https://opencode.j9xym.com/api/share -H "Content-Type: application/json" -d '{"sessionID":"test123"}' — if you get HTML or 403, that's it.
2. URL cosmetic issue (line 78)
Now that API_DOMAIN is correctly https://opencode.j9xym.com, the returned URL is https://opencode.j9xym.com/api/share/{id} — but this is the JSON data endpoint. The human-viewable HTML page is at /share/{id}. The test expects /share/ not /api/share/. This doesn't break sync but means the URL printed to the user opens raw JSON instead of the rendered session.
3. No fetch error handling in share-next.ts
Neither create(), sync(), nor remove() check response.ok. Any failure (WAF, timeout, 500) is silently swallowed.