ci: route jobs to secure self-hosted Podman runners - #1
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Free Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (43)
📝 WalkthroughWalkthroughThe PR updates core compaction/context plumbing, plugin and workspace presence handling, and TUI/web flows for ask, compact, and context actions. It also rewrites documentation, CI, and the Windows installer around the new runner and workflow setup. ChangesCore runtime plumbing
TUI and web command flow
Docs, skills, CI, and installer updates
Estimated code review effort: 5 (Critical) | ~120 minutes Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
The Go TUI defined parseAskRequest/handleAskKey/renderAskOverlay in ask.go
but never wired them into the three integration points, so the core's
ask_request event was ignored: no flyout opened, s.pendingAsk stayed nil, and
the core blocked forever on an answer that never came. The ask call rendered
as a plain tool block ("▸ ask (...)") with no modal.
Three wiring fixes:
- handleCoreEvent: add `case "ask_request"` that parses questions via
rawKey (ev.get unmarshals arrays into a string and returns "" — the
original bug) and sets s.pendingAsk.
- handleKey: dispatch to handleAskKey when s.pendingAsk is set, before
scroll/global keys (mirrors the modal intercept).
- View: call renderAskOverlay in the view assembly (no-op when nil).
Also fixes:
- Navigation: handleAskKey used unregistered action names
("next_field"/"down"/"prev_field"/"up") so s.kb always returned false and
up/down never moved between questions. Now uses field_next/field_prev +
bare "down"/"up" + nav_down/nav_up_alt (j/k) fallbacks.
- UX: Enter on an empty required field used to logError a fresh "✗ required"
transcript line per press (spam). Now shows a transient inline a.errMsg in
the flyout, cleared on the next non-submit keypress.
Adds tui/ask_test.go with 6 regression tests covering the event/render/
submit/skip/navigation/inline-error paths.
Adds two skills: setup-self-hosted-gh-runner (secure ephemeral rootless
Podman runner) and wire-tui-blocking-flyout (the pattern this commit applies).
Plugin system reaches feature parity with direct core edits (no recompile): - override:true replaces a built-in tool's implementation - disable_tools removes tool names from the model's toolset (strongest lever) - system_prompt field injects static text into the system prompt - catch-all pre_tool/post_tool hooks fire for EVERY tool call - post hooks can modify results (output/ok/diff): redact, append, reformat - refactored pre/post dispatch into run_pre_hooks/run_post_hooks Context management: - auto_compact toggle (default true; env CATALYST_CODE_AUTO_COMPACT) - compact_instructions customizes the summarize prompt (/compact <instr>) - compacting event surfaces a toast before the summarize call - /context command + context_breakdown event: token-usage breakdown with top consumers (TUI modal + web toast); threshold 70% -> 90% @<path> file mentions now inline referenced file contents into the prompt (no read_file round-trip), mirroring how /skill inlines a skill body. Approval::Never now disables path CONFINEMENT too (resolve_unconfined), not just the dangerous-path list — absolute / .. / symlink paths are allowed when the model is fully trusted. README rewritten (more concise, reorganized).
Add read-only cross-session awareness so an agent can tell whether a phantom error / unexpected file change came from a neighbor before "fixing" it and corrupting in-flight work (L1 awareness — no coordination/locking). - presence.rs: per-pid JSON records under ~/.config/catalyst-code/presence/<hash(cwd)>/, atomically written (temp+fsync+rename), stale records reaped by mtime on read (tolerates kill -9). Each process owns one file → zero write contention. - main.rs: heartbeat task publishes work-state every ~8s and refreshes a cached peer snapshot (no filesystem read on the hot path). Shutdown clears our own record. maybe_concurrency_note appends a nudge to tool results when another session is active AND a tool failed or touched a file a peer is editing — rate-limited (60s) per session. - tools.rs: new ReadOnly `workspace_activity` tool listing other active sessions (goal / in-progress / recently-touched files / model). - Cargo: add `filetime` dev-dependency for the stale-reaping test. Tests: presence write/read + stale reap, peers_touching path matching, workspace_activity peer listing + self-exclusion. clippy clean. Also add two skills (add-core-background-task, production-readiness-review) and update git-commit-all to verify builds before committing + read full diffs for large multi-feature changes.
Applies the P1 findings from a production-readiness review across the core and TUI. Security: - fetch: SSRF hardening — block private/loopback/link-local ranges (incl. cloud-metadata 169.254.169.254) by default under an empty allowlist; an explicit fetch_allowlist entry overrides (operator opt-in). IPv6 literals now parsed correctly. (core/src/fetch_tool.rs) - config: chmod 0o600 providers config.json + 0o700 parent dir to keep stored credentials tight. (core/src/config.rs) - plugins: document that pre-hook `modify` runs after the approval gate (pre-hooks are trusted, user-installed code). (core/src/plugins.rs) TUI lifecycle + data-race fixes: - coreProcess is now an atomic.Pointer (shared across the UI + signal goroutines — a plain var was a data race). - Quit/SIGHUP/SIGTERM sets a `quitting` flag so the core's stdout EOF (from the kill) doesn't trigger an auto-restart during teardown. The signal handler sends a sigtermMsg so Bubble Tea restores the terminal via tea.Quit instead of a raw os.Exit that leaves it broken. - Startup watchdog: surface a clear error if the core never emits `ready` within 30s (bad UMANS_CORE path / panic), generation-tagged so a stale tick from a previous (crashed) core is ignored. - Reap zombie openURL children (Run in a goroutine); stop double- Wait()ing the core on quit (the reader goroutine reaps via cmd.Wait). - busy=false on reset/history (conversation boundaries) so a mid-turn /load or /sessions can't wedge the TUI. - OSC 52 clipboard write routed through a tea.Cmd to serialize with the renderer (a direct os.Stdout write races it and garbles the screen). - modelIdx=-1 sentinel for a no-models core (downstream guards accept it). Memory + UX: - Cap stored tool-result text at 256 KiB (capOutput) so one multi-MB result can't pin memory for the session; the renderer already truncates the visible portion. (tui/blocks.go, handlers.go) - Mention @-completion walk runs in a background goroutine (was a synchronous walk that froze the UI on large repos); `walking` flag dedupes concurrent walks. (tui/mention.go) CI + community: - Add a gofmt gate to the TUI CI job. (.github/workflows/ci.yml) - Add CODE_OF_CONDUCT.md + CONTRIBUTING.md. - New ttft-prompt-caching-audit skill; fix-all section in the production-readiness-review skill. Numerous rustfmt/gofmt cleanups throughout (now enforced by CI gates).
Introduce core/src/fsutil.rs centralizing two cross-process hazards that bite any multi-process run (two TUI sessions, TUI + web, parallel CI): - unique-temp atomic writes: every temp file gets a pid+random name so two concurrent writers never share a temp (a fixed `foo.json.tmp` interleaves writes and renames a corrupted file over the target). - FileLock: advisory flock(2) on a sidecar .lock file, auto-released on exit/crash (no stale locks), serializing read-modify-write sections. Apply both across every shared-file writer: - config.rs (providers config.json): flock RMW + atomic_write_secure - memory.rs (append): flock RMW + unique-temp write - oauth.rs (gemini/codex/token stores x3): atomic_write_secure - pattern_log.rs (append): flock RMW + unique-temp write - provider.rs (models cache): flock RMW + unique-temp write - staging.rs: unique-temp write (idempotent, no lock needed) - tui/settings.go: unique-temp write (os.CreateTemp) Adds the `libc` crate for flock on Unix.
- README.md: restructure to Best-README-Template layout (badges, TOC, Getting Started, Roadmap, License, Contact) with reference-style links; document the one-line curl/irm installers and the new install.ps1. - install.ps1: new top-level Windows installer — downloads the prebuilt standalone catcode.exe (core embedded) from GitHub Releases, optional -WithWeb service install, -Update/-Uninstall/-DryRun, no admin.
Add guidance to run the authoritative CI gates (cargo fmt --check, clippy -D warnings, gofmt -l, go test -race) rather than just "it compiles", and to run any newly-added gate before committing.
What
Route CI jobs to a secure self-hosted runner (ephemeral rootless Podman containers on the dev box) instead of GitHub-hosted
ubuntu-latest, which were slow (cold caches + queue/provisioning time).How the runner works
--rm'd after the job — no persistence for backdoors, no root daemon, isolated from the host's home dir / secrets / other services. Containerrootis mapped to the unprivileged host user via the user namespace./cachevolume (warm Rust/Go/build caches → fast rebuilds).gh-runner.service, lingered) supervises a pool that mints a fresh 1-hour registration token per spawn viagh api. The high-valueghPAT stays on the host supervisor and never enters the containers.Changes in this PR
runs-on: ubuntu-latest→runs-on: [self-hosted, Linux, X64].docker imagejob: replaced Docker buildx (setup-buildx-action+build-push-action) withbuildah build --isolation chroot --storage-driver vfs— nested OCI build that works under rootless Podman without extra privileges (build-check only, no push).Notes
release.ymlis intentionally left onubuntu-latestfor now — it needs exotic packaging tools (AppImage/FUSE, xorriso DMG, msitools MSI) that may not all work in a rootless container; converting it is a follow-up.~/gh-self-hosted/(outside the repo) with a runbook at~/gh-self-hosted/README.md.Summary by CodeRabbit
/contextmodal showing context token-usage breakdown, plus Auto Compact and/compactinstruction support.