Session log improvements + mobile reconnect fix#91
Merged
itscooleric merged 43 commits intomainfrom Mar 18, 2026
Merged
Conversation
groupadd fails with exit 4 if the GID already exists. Fall back to renaming the existing group to 'clide' so the useradd step succeeds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the npm-installed Claude Code CLI with the official native installer (curl -fsSL https://claude.ai/install.sh | sh). This eliminates the auto-update permission error that occurred because the npm global prefix was owned by root while claude runs as clide. The native binary at ~/.local/bin/claude is self-updating — no more version pinning or container rebuilds needed to get new Claude releases. Node.js is retained for Codex CLI and entrypoint config scripts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…41, #42, #44) Add session-logger.sh that wraps agent CLI sessions with: - JSONL event logging (session_start/session_end) with schema_version=1 - Raw transcript capture via `script` command, gzipped on exit - Secret scrubbing (blocklist + heuristic) on all logged output - Automatic retention: prune oldest sessions beyond CLIDE_MAX_SESSIONS (default 30) - Agent-agnostic: works with claude, codex, copilot, or any command Wired into claude-entrypoint.sh so all agent sessions are logged automatically. Disable with CLIDE_LOG_DISABLED=1. Adds docs/schema/session-events-v1.md documenting the event format. Closes #41, closes #42, closes #44 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The install script from claude.ai uses bash syntax (parentheses in conditionals) which fails under dash (Ubuntu's default sh). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bump scrollback 10k → 50k (long agent output) - Zero escape-time (no vim lag) - Enable focus-events + OSC 52 clipboard - F12 toggles mouse mode (mobile-friendly) - Dark theme status bar with active command display - Subtle pane borders - Fix reload bind to use ~/.tmux.conf (was /root/) - ttyd --reconnect 3 for auto-reconnect on disconnect Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds notify.sh — background watcher that tails the transcript and sends push notifications via ntfy when: - Agent needs approval (permission prompts) - Agent needs input - Errors occur - Task completes 30s cooldown between notifications to avoid spam. Fully opt-in via CLIDE_NTFY_URL env var. Works with any self-hosted or public ntfy instance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ttyd 1.7.7 doesn't have --reconnect as a server flag — it's a client-side option passed via --client-option reconnect=N. This was causing exit code 254 crash loops. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents how to move auth from ttyd --credential (leaks password to process args and docker logs) to Caddy reverse proxy layer. Labels go in docker-compose.override.yml since they can't be conditional. Co-Authored-By: Claude Opus 4.6 <parameter>noreply@anthropic.com>
ttyd prints --credential args in its startup banner, leaking passwords into `docker logs`. Filter output through sed to redact TTYD_PASS. Simpler than moving auth to Caddy (bcrypt $ breaks compose interpolation) and keeps auth config in one place (.env). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ttyd 1.7.7 prints the credential both as plaintext and base64 in its startup banner. Scrub both forms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Piping exec through sed made sed PID 1 instead of ttyd, breaking health checks and signal handling. Revert to clean exec. ttyd logs the credential as base64 which is acceptable since docker logs requires host access. Unset TTYD_PASS from env so child processes (shells, agents) can't read it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Downloads and trusts a CA certificate on container startup so internal TLS services (ntfy, gitlab, etc.) work with https. Graceful — logs a warning and continues if the download fails. Skips if already installed (avoids duplicate work when entrypoint.sh calls claude-entrypoint.sh). Set CLIDE_CA_URL in .env to the URL of your CA cert. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Raw terminal transcripts are too noisy (ANSI escapes, partial writes) for reliable pattern matching. Replaced background tail -f watcher with simple event-based calls: start, end, error. Session logger fires notifications directly at session boundaries. Approval-prompt notifications will require structured output (--output-format stream-json or SDK) — tracked as future work. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The web entrypoint runs claude-entrypoint.sh true to pre-seed config. This was creating spurious sessions and firing false start/end notifications. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Typing claude/codex/copilot in any shell now automatically goes through session-logger.sh for structured logging and notifications. No need to remember session-logger.sh prefix. Disable with CLIDE_LOG_DISABLED=1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bump version banner to v4 - Add session logging section with env vars - Add push notifications (ntfy) section - Add LAN CA certificate section - Add F12 mouse toggle to tmux shortcuts - Document auto-reconnect - Add session events schema to docs table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SC2148: add shellcheck directive to .bashrc - SC2086: disable for intentional word splitting of AGENT_CMD - SC2012: disable for intentional ls -1dt sorting by mtime - MD040: add language to fenced code block in README Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: v4 agent observability — logging, notifications, terminal improvements
Enables SSH-based remote build/deploy workflow from clide to forge-edge VPS. Previous session added these to the Dockerfile but never committed, so rebuilds used the stale committed version without these packages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The `script` command captures raw VT100 byte streams which are unreadable for TUI apps like Claude Code (29K+ ANSI escapes, 15K+ carriage returns, character-by-character cursor positioning). Instead, harvest Claude Code's native JSONL conversation logs from ~/.claude/projects/<project>/<uuid>.jsonl after session end. These contain structured user messages, assistant responses (with thinking blocks), tool_use calls, and tool_results — actually useful data. Changes: - Snapshot Claude session files before run, diff after to find new ones - Symlink conversation.jsonl into clide session directory - Demote raw script capture to opt-in (CLIDE_RAW_TRANSCRIPT=1) - Rename transcript.txt → transcript.raw (signals it's not readable) - session_end event now includes claude_session_id and has_conversation Addresses #42 (transcript capture) and #41 (structured logging spike). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bake branch@hash (date) into the Docker image via BUILD_VERSION arg, show it in the CLI splash banner and web entrypoint logs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The session-logger cleanup code (conversation harvesting, session_end event, transcript compression) was unreachable when the agent was killed rather than exiting cleanly. This caused events.jsonl to only contain session_start, conversation.jsonl to never be linked, and raw transcripts to remain uncompressed. Move all cleanup into a trap handler for EXIT/INT/TERM/HUP so it runs regardless of how the session ends. Also show the splash banner in .bashrc so it appears in the ttyd web terminal on connect. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of waiting until session cleanup to discover and symlink the Claude Code conversation JSONL, spawn a lightweight background watcher that polls for up to 60s until the file appears, then links it immediately. Since Claude Code writes to this file continuously, the conversation log is readable live throughout the entire session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Symlinks pointed to paths inside the clide container that don't exist on the host or in other containers (e.g. Clem file explorer). Now: - Background watcher copies the file and re-syncs every 30s - Cleanup does a final copy instead of symlink - Source path stored in .conv_source for cleanup to find Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code creates its JSONL session files with 600 permissions. When session-logger.sh copies them, the restrictive permissions are preserved, causing CLEM's file explorer to get 403 Forbidden. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: v4 agent observability + SSH remote deploy
- UID/GID default changed from 1000 to 1100 to avoid conflict with Docker group (GID 1000) common on Ubuntu hosts - CPU limit now configurable via CLIDE_CPUS env var (default: 2.0, was hardcoded 4.0 which fails on 2-core VPS) - Memory limit configurable via CLIDE_MEM_LIMIT (default: 4g) - Port binding configurable via CLIDE_BIND (default: 0.0.0.0, set to Tailscale IP for VPN-only access) - .env.example updated with all new vars Closes #82 Co-authored-by: itscooleric <itscooleric@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parses Claude Code conversation JSONL for token usage data and adds it to the session_end event in events.jsonl: - input_tokens, output_tokens, total_tokens - estimated_cost_usd (per-model pricing: Opus, Sonnet, Haiku) - turns count New script: scripts/token-cost.py — standalone parser, can also be run manually on any conversation.jsonl file. Closes #43 Co-authored-by: itscooleric <itscooleric@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: outbound connection audit for agent sessions (#51) New egress-audit.sh daemon monitors /proc/net/tcp and logs all outbound connections to .clide/logs/egress.jsonl with: - Remote IP, hostname, port - Local port, UID - Timestamp, verdict (allow/reject) Opt-in via CLIDE_EGRESS_AUDIT=1 in .env. Starts automatically from firewall.sh when enabled. Closes #51 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: filter private/Docker IPs from egress audit log Only log public internet connections — skip 127.x, 172.x, 10.x, 192.168.x Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: itscooleric <itscooleric@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mitmproxy-based HTTP(S) interceptor that logs all agent traffic to .clide/logs/intercept.jsonl with: - Method, URL, host, path, status code - Request/response headers (secrets auto-redacted) - Request/response sizes, duration - Optional body capture (CLIDE_INTERCEPT_BODIES=1) Opt-in via CLIDE_INTERCEPT=1 in .env. Starts automatically before the agent in claude-entrypoint.sh, sets HTTP(S)_PROXY env vars so agent tools route through the proxy transparently. Includes secret redaction for API keys, tokens, and auth headers. Closes #50 Co-authored-by: itscooleric <itscooleric@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: ignore-file leakage verification harness + v4 docs (#52) Test harness to verify AI agents don't leak gitignored secrets: - setup-fixture.sh: creates synthetic repo with unique markers in .env, credentials.json, *.pem, secrets/, node_modules/ - check-leakage.py: scans intercept/egress/conversation logs for marker strings — exits 0 (clean) or 1 (leaked) - run-test.sh: full end-to-end test runner Also adds docs/observability.md documenting the full v4 stack: session logging, token tracking, egress audit, intercept proxy, leakage testing, firewall, MITM cert trust, secret redaction. Closes #52 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: shellcheck SC2188 — add true before redirection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: propagate proxy env to ttyd + install mitmproxy CA cert - Start intercept proxy in entrypoint.sh (not just claude-entrypoint) so web terminal sessions also route through the proxy - Install mitmproxy CA cert system-wide + set NODE_EXTRA_CA_CERTS so Node.js (Claude Code) trusts the MITM HTTPS interception - Wait 2s for mitmproxy to generate its CA before proceeding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: itscooleric <itscooleric@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#89, #90) Intercept and egress logs now write into the per-session directory instead of a single global file, so they get pruned with session retention and are easy to correlate. Session directory names use UTC datetime (clide-20260318-143022-xxxx) instead of base36 timestamps. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Increase reconnect delay from 3s to 10s and ping interval from 5s to 30s. Mobile browsers aggressively kill background WebSockets — the short ping interval caused the server to drop the connection quickly, and the tight 3s retry loop created visible flicker without stabilizing. Both values are now configurable via TTYD_RECONNECT and TTYD_PING_INTERVAL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In ttyd 1.7.7, the "reconnect" client option is a DISABLE flag — any truthy value (like "3" or "10") turns auto-reconnect OFF. The old config `--client-option reconnect=3` was actually disabling reconnect, causing "Press Enter to reconnect" on mobile instead of auto-reconnecting. Fix: remove the reconnect client option entirely (auto-reconnect is on by default). Only pass it when user explicitly sets TTYD_RECONNECT=0. Keep the 30s ping interval to give mobile browsers breathing room. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ttyd's built-in basic auth (--credential) is broken on all iOS browsers and Safari due to Apple's NSURLSession WebSocket implementation not passing Basic Auth credentials on the WebSocket upgrade request (ttyd upstream #1437). Add TTYD_AUTH_PROXY=true mode: delegates authentication to a reverse proxy (Caddy/nginx) via --auth-header, so ttyd never handles auth itself and the WebSocket connection works on all browsers. Also adds caddy network to web service and documents Caddy basic_auth label setup in docker-compose.yml comments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Caddy basic_auth validates credentials but doesn't automatically
forward the username. ttyd's --auth-header mode requires the header
to be present. Add header_up to pass {http.auth.user.id} as X-Auth-User.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Base docker-compose.yml now works standalone with ttyd's built-in auth (no Caddy dependency). Caddy reverse proxy setup (networks, labels, basic_auth) lives entirely in docker-compose.override.yml, documented in the .example file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove individual agent services (claude, copilot, codex, gh, shell). All CLIs are available from any shell — just type the command. Two services remain: - web: browser terminal (ttyd + tmux) - cli: headless shell (docker compose run --rm cli) Simplifies Makefile, wrapper script, VS Code tasks, and README. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…auth Rewrite DEPLOY.md to reflect current setup: two services (web/cli), Caddy basic_auth proxy for mobile, no more CADDY_HOSTNAME/CADDY_TLS env vars. Update RUNBOOK.md references from shell/claude to cli. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
clide-20260318-143022-xxxx)TTYD_AUTH_PROXY=truemodeweb+cli), remove individual agent services. -194 linesWhat changed
web,shell,claude,copilot,codex,ghweb,cliclide-mmprafvt-d85a5cd0clide-20260318-143022-d85a5cd0intercept.jsonl--credential(broken on iOS)--auth-headerKey files
entrypoint.sh—TTYD_AUTH_PROXYmode, ping interval tuning, reconnect fixdocker-compose.yml— simplified toweb+cli, no Caddy dependencydocker-compose.override.yml.example— full Caddy setup docsscripts/session-logger.sh— datetime session IDs +CLIDE_SESSION_DIRexportscripts/intercept-proxy.py/egress-audit.sh— per-session log outputTest plan
./clide cliandmake clilocallyTTYD_USER/TTYD_PASSauth)🤖 Generated with Claude Code