Repository navigation
feat(boot): token safety, SELF_HOOK install guard, and a tee'd diag log - #38
Conversation
…sn't installed Three first-run improvements: - Token safety: the bearer token was printed in plaintext on the `Token:` line and in every `Scan to connect · <url>` (LAN, public base, and each tunnel in expose.ts), so it lingered in scrollback / screen-shares / captured logs. New connect-url.ts redacts it: the Token line is masked (first4…last4), the connect URL is printed token-redacted, and the full URL (with the token) goes only into the QR — printed in plaintext only with no QR to scan or SHOW_TOKEN=1 for manual entry. - SELF_HOOK guard: hooksInstalled() checks each agent's config for our marker; with SELF_HOOK=1 but nothing installed, the server now warns to run hook-install (else reports silently never arrive and no pane cuts over). - Friendlier "no agent yet" hint (picked up automatically, no restart). Unit-tested (redactToken/maskToken; hooksInstalled install/uninstall round-trip); boot-verified the masked output and the SELF_HOOK warning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELxUVZ1WcJWfYbAdY5uMs4
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 931ec3a9aa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export function hooksInstalled(): { claude: boolean; codex: boolean } { | ||
| const claude = readJsonSafe(claudeSettingsPath()); | ||
| const codex = readJsonSafe(codexHooksPath()); | ||
| return { claude: !!claude && anyOurHook(claude), codex: !!codex && anyOurHook(codex) }; |
There was a problem hiding this comment.
Check Codex hook feature before reporting it active
When SELF_HOOK=1 and $CODEX_HOME/hooks.json contains this marker but config.toml lacks [features] hooks = true, this reports codex: true even though codexHooksFeatureEnabled() documents that Codex will not load hooks.json unless the feature is enabled. In that setup no Codex reports arrive, but startup can skip the “no reports” warning or even say only Codex will report, leaving the pane silently not cutting over.
Useful? React with 👍 / 👎.
…nosis
The [hook]/[bridge]/[sse] lines and the boot banner went to the terminal only, so diagnosing
a live issue meant re-running with stdout redirected. installConsoleTee() now mirrors
console.{log,info,warn,error} to a diag log (default /tmp/even-better-<pid>.log, or
CONSOLE_LOG_FILE), timestamped and token-redacted, on unless LOG=off — separate from the JSON
event log so tools/ parsers stay clean. redactString also masks the "Token : <value>" banner
line (colon form; the token= URL form was already handled) so the token never lands in the
diag log. The banner prints the diag-log path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELxUVZ1WcJWfYbAdY5uMs4
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 453cee9296
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| orig(...args); | ||
| try { | ||
| const text = args.map((a) => (typeof a === "string" ? a : String(a))).join(" "); | ||
| appendFileSync(consoleLogPath, `${new Date().toISOString()} ${tag} ${redactString(text)}\n`); |
There was a problem hiding this comment.
Avoid teeing the full-token QR to disk
With the default LOG mode and QR=1, printConnect() sends the QR block generated from the full URL (including the bearer token) through console.log, and this wrapper persists that block verbatim to consoleLogPath. redactString() cannot remove a token once it is encoded as QR glyphs, so the new diagnostic log still contains a decodable credential even though the human-readable URL is redacted.
Useful? React with 👍 / 👎.
…out of the diag log - hooksInstalled().codex now requires `[features] hooks = true` too — Codex never loads hooks.json without the feature, so a present-but-inert marked hook must not report as installed (else the SELF_HOOK "no reports" warning is wrongly skipped). - printConnect writes the QR straight to stdout (not console.log), so the diag-log tee no longer persists the QR glyphs — which encode the full bearer token and are decodable even though the printed URL is redacted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELxUVZ1WcJWfYbAdY5uMs4
|
Both fixed in 26a6ed0. Codex feature gate (P2): QR token in the diag log (P1): good catch —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26a6ed05e6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const codex = readJsonSafe(codexHooksPath()); | ||
| return { | ||
| claude: !!claude && anyOurHook(claude), | ||
| codex: !!codex && anyOurHook(codex) && codexHooksFeatureEnabled() === true, |
There was a problem hiding this comment.
Require Codex trust before reporting active
When SELF_HOOK=1 and the user has written hooks.json plus [features] hooks = true but has not completed Codex's /hooks trust step, this returns codex: true; however this same module documents that Codex skips non-managed command hooks until the exact definition is trusted (see src/hook-install.ts lines 178-182), so no reports will arrive. In that setup the new startup guard can suppress the “no reports” warning or say only Codex will report even though the pane still never cuts over.
Useful? React with 👍 / 👎.
…fiable Even with hooks.json + [features] hooks = true, Codex won't run the hook until the user trusts the exact definition via /hooks (a trusted_hash we deliberately don't forge and can't recompute to verify). So the SELF_HOOK guard no longer says "only Codex will report"; it flags a missing Claude install, and whenever Codex hooks are installed it prints an honest reminder that they report only once trusted via /hooks (not verifiable here). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELxUVZ1WcJWfYbAdY5uMs4
|
Fixed in abf1ec2 — right, Codex's |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: abf1ec2bf9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } else if (!inst.claude) { | ||
| console.log(" ⚠ SELF_HOOK=1 — Claude hooks not installed; re-run hook-install."); |
There was a problem hiding this comment.
Warn when Codex hooks are inert
When SELF_HOOK=1, Claude hooks are installed, and Codex hooks are missing or inert (for example $CODEX_HOME/config.toml lacks [features] hooks = true), this branch skips all warnings because !inst.claude is false and inst.codex is also false. For a Codex-only pane no Codex reports arrive and the bridge never cuts over; fresh evidence is that the new startup guard now computes inst.codex correctly but never surfaces codex: false unless Claude is also missing.
Useful? React with 👍 / 👎.
|
|
||
| /** First4…last4 of a token, so the Token line identifies it without printing the secret. */ | ||
| export function maskToken(t: string): string { | ||
| return t.length > 8 ? `${t.slice(0, 4)}…${t.slice(-4)}` : "…"; |
There was a problem hiding this comment.
Hide short custom tokens completely
When BRIDGE_TOKEN is a short human-chosen value, this masks only the middle after eight characters, so a token like test-token is printed as test…oken in the startup banner and leaves only a couple of characters unknown. If the LAN/public URL is visible to the same viewer, they can brute-force the remaining token and drive the agent; please fully hide short custom tokens or require a much higher length before showing prefixes/suffixes.
Useful? React with 👍 / 👎.
…om tokens - The SELF_HOOK guard skipped the Codex warning whenever Claude was installed, so a codex-only pane with inert Codex hooks (no feature) was silently missed. Warn per agent that isn't set up, not only when both are missing. - maskToken revealed first4…last4 for any token > 8 chars, so a short custom BRIDGE_TOKEN like `test-token` printed as `test…oken` — brute-forceable against the visible URL. Only reveal ends for a long token (>= 24, the ephemeral one); hide a short one whole. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELxUVZ1WcJWfYbAdY5uMs4
|
Both fixed in 6405da4. Codex-only pane missed (index.ts:569): the guard only warned about Codex when Claude was also missing. Now it warns per agent that isn't set up — a codex-only pane with inert Codex hooks (marker present but feature off) gets its own warning even when Claude is installed. Verified. Short custom token (connect-url.ts:22):
|
First-run / diagnosis improvements from live testing.
1. Token safety
The bearer token was printed in plaintext on the
Token:line and everyScan to connect · <url>(LAN, public base, each tunnel inexpose.ts), lingering in scrollback / screen-shares / logs. Newconnect-url.ts:Token:masked (93b8…9206); the connect URL printed token-redacted; the full URL goes only into the QR (printed plaintext only with no QR, orSHOW_TOKEN=1).2. SELF_HOOK install guard
SELF_HOOK=1drives bridges only if our hook is installed.hooksInstalled()checks each agent's config; the server warns (run hook-install) when SELF_HOOK is on but nothing is installed.3. Tee'd diag log
[hook]/[bridge]/[sse]and the banner went to the terminal only.installConsoleTee()now mirrorsconsole.*to a diag log (default/tmp/even-better-<pid>.log, orCONSOLE_LOG_FILE), timestamped + token-redacted, on unlessLOG=off— separate from the JSON event log.redactStringalso masks theToken : <value>banner line, so the token never lands in the diag log.4. Friendlier "no agent yet" hint
Verification
pnpm checkclean;pnpm test→ 196 pass (redactToken/maskToken, hooksInstalled round-trip). Boot-verified: masked token, redacted connect URL, SELF_HOOK warning, and diag-log capture withToken : [REDACTED].🤖 Generated with Claude Code