Skip to content

feat(boot): token safety, SELF_HOOK install guard, and a tee'd diag log - #38

Merged
pawaca merged 5 commits into
mainfrom
feat/boot-ux-token-safety
Jul 13, 2026
Merged

pawaca merged 5 commits into
mainfrom
feat/boot-ux-token-safety

Conversation

@pawaca

@pawaca pawaca commented Jul 13, 2026 •

Copy link
Copy Markdown
Owner

First-run / diagnosis improvements from live testing.

1. Token safety

The bearer token was printed in plaintext on the Token: line and every Scan to connect · <url> (LAN, public base, each tunnel in expose.ts), lingering in scrollback / screen-shares / logs. New connect-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, or SHOW_TOKEN=1).

2. SELF_HOOK install guard

SELF_HOOK=1 drives 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 mirrors console.* to a diag log (default /tmp/even-better-<pid>.log, or CONSOLE_LOG_FILE), timestamped + token-redacted, on unless LOG=off — separate from the JSON event log. redactString also masks the Token : <value> banner line, so the token never lands in the diag log.

4. Friendlier "no agent yet" hint

Verification

pnpm check clean; pnpm test → 196 pass (redactToken/maskToken, hooksInstalled round-trip). Boot-verified: masked token, redacted connect URL, SELF_HOOK warning, and diag-log capture with Token : [REDACTED].

🤖 Generated with Claude Code

…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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/hook-install.ts Outdated
export function hooksInstalled(): { claude: boolean; codex: boolean } {
const claude = readJsonSafe(claudeSettingsPath());
const codex = readJsonSafe(codexHooksPath());
return { claude: !!claude && anyOurHook(claude), codex: !!codex && anyOurHook(codex) };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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
@pawaca pawaca changed the title feat(boot): keep the token out of the terminal; warn when SELF_HOOK isn't installed feat(boot): token safety, SELF_HOOK install guard, and a tee'd diag log Jul 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/log.ts
orig(...args);
try {
const text = args.map((a) => (typeof a === "string" ? a : String(a))).join(" ");
appendFileSync(consoleLogPath, `${new Date().toISOString()} ${tag} ${redactString(text)}\n`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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
@pawaca

pawaca commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

Both fixed in 26a6ed0.

Codex feature gate (P2): hooksInstalled().codex now also requires [features] hooks = true — Codex never loads hooks.json without it, so a present-but-inert marked hook no longer reports as installed, and the SELF_HOOK "no reports" warning fires correctly when the feature is off.

QR token in the diag log (P1): good catch — redactString can't scrub a token encoded as QR glyphs. printConnect now writes the QR via process.stdout.write instead of console.log, so the tee (which wraps console.*) never persists it. Verified: 0 QR glyph lines in the diag log, and Token/token= both show [REDACTED].

pnpm check + 196 tests green (hooksInstalled test now covers the feature-off → codex:false case). 👍

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/hook-install.ts
const codex = readJsonSafe(codexHooksPath());
return {
claude: !!claude && anyOurHook(claude),
codex: !!codex && anyOurHook(codex) && codexHooksFeatureEnabled() === true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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
@pawaca

pawaca commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

Fixed in abf1ec2 — right, Codex's /hooks trust is the third requirement and it's unverifiable from here (the trusted_hash is codex-internal; we deliberately don't forge it). So the guard no longer claims "only Codex will report." It warns on a missing Claude install, and whenever Codex hooks are installed (marker + feature) it prints an honest reminder that they report only once trusted via /hooks (not verifiable here) rather than implying they're active. pnpm check + 196 tests green. 👍

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/index.ts Outdated
Comment on lines +568 to +569
} else if (!inst.claude) {
console.log(" ⚠ SELF_HOOK=1 — Claude hooks not installed; re-run hook-install.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/connect-url.ts Outdated

/** 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)}` : "…";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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
@pawaca

pawaca commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

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): maskToken revealed first4…last4 for anything > 8 chars, so test-token → test…oken. Now it only reveals ends for a long token (>= 24, i.e. the 48-char ephemeral one) and hides a short custom BRIDGE_TOKEN whole (…).

pnpm check + 196 tests green (maskToken threshold cases added). 👍

@pawaca
pawaca merged commit 1157f49 into main Jul 13, 2026
1 check passed
@pawaca
pawaca deleted the feat/boot-ux-token-safety branch July 13, 2026 03:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant