Skip to content

ci: route jobs to secure self-hosted Podman runners - #1

Merged
karutoil merged 9 commits into
masterfrom
ci/self-hosted-podman-runners
Jul 8, 2026
Merged

ci: route jobs to secure self-hosted Podman runners#1
karutoil merged 9 commits into
masterfrom
ci/self-hosted-podman-runners

Conversation

@karutoil

@karutoil karutoil commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

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

  • Ephemeral rootless Podman: each job runs in a fresh container that is --rm'd after the job — no persistence for backdoors, no root daemon, isolated from the host's home dir / secrets / other services. Container root is mapped to the unprivileged host user via the user namespace.
  • 2 concurrent slots, each with its own persistent /cache volume (warm Rust/Go/build caches → fast rebuilds).
  • A systemd user service (gh-runner.service, lingered) supervises a pool that mints a fresh 1-hour registration token per spawn via gh api. The high-value gh PAT stays on the host supervisor and never enters the containers.
  • Toolchains (Rust + clippy/rustfmt, Go 1.24, buildah) are baked into the image.

Changes in this PR

  • All jobs: runs-on: ubuntu-latestruns-on: [self-hosted, Linux, X64].
  • docker image job: replaced Docker buildx (setup-buildx-action + build-push-action) with buildah build --isolation chroot --storage-driver vfs — nested OCI build that works under rootless Podman without extra privileges (build-check only, no push).

Notes

  • release.yml is intentionally left on ubuntu-latest for 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.
  • First run after this merges will be cold (caches empty); subsequent runs are fast.
  • Runner infra lives in ~/gh-self-hosted/ (outside the repo) with a runbook at ~/gh-self-hosted/README.md.

Summary by CodeRabbit

  • Chores
    • Updated CI and container image builds to use self-hosted Linux x64 runners and a rootless-friendly Buildah flow.
  • New Features
    • Added a blocking “ask” modal flyout with submit/skip handling and inline validation.
    • Introduced a /context modal showing context token-usage breakdown, plus Auto Compact and /compact instruction support.
    • Added “workspace activity” to display other active sessions.
  • Bug Fixes & Tests
    • Improved ask keyboard navigation and focus; added regression tests for ask interactions.
    • Truncate very large tool outputs to keep the UI responsive.
  • Documentation
    • Added secure self-hosted runner and blocking ask flyout documentation.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 16ba764a-959a-4c6b-b4d9-af80770f3bd9

📥 Commits

Reviewing files that changed from the base of the PR and between 2c92b9c and 5da7ce2.

⛔ Files ignored due to path filters (1)
  • core/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (43)
  • .catalyst-code/skills/add-core-background-task/SKILL.md
  • .catalyst-code/skills/git-commit-all/SKILL.md
  • .catalyst-code/skills/production-readiness-review/SKILL.md
  • .catalyst-code/skills/ttft-prompt-caching-audit/SKILL.md
  • .github/workflows/ci.yml
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • README.md
  • core/Cargo.toml
  • core/src/config.rs
  • core/src/fetch_tool.rs
  • core/src/fsutil.rs
  • core/src/main.rs
  • core/src/memory.rs
  • core/src/oauth.rs
  • core/src/pattern_log.rs
  • core/src/plugins.rs
  • core/src/presence.rs
  • core/src/protocol.rs
  • core/src/provider.rs
  • core/src/staging.rs
  • core/src/subagent.rs
  • core/src/tools.rs
  • core/src/workspace.rs
  • install.ps1
  • tui/blocks.go
  • tui/extras_test.go
  • tui/handlers.go
  • tui/keybinds.go
  • tui/keybinds_test.go
  • tui/login_key_entry_test.go
  • tui/main.go
  • tui/mention.go
  • tui/mention_test.go
  • tui/modal.go
  • tui/pin_test.go
  • tui/protocol.go
  • tui/settings.go
  • web/src/components/chat.tsx
  • web/src/lib/commands.ts
  • web/src/lib/reducer.ts
  • web/src/lib/types.ts
  • web/src/lib/use-agent.ts

📝 Walkthrough

Walkthrough

The 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.

Changes

Core runtime plumbing

Layer / File(s) Summary
Atomic writes and locked persistence
core/src/fsutil.rs, core/src/config.rs, core/src/memory.rs, core/src/oauth.rs, core/src/pattern_log.rs, core/src/staging.rs, core/src/provider.rs
Shared filesystem helpers are added and then used for secure atomic writes and cross-process locking across config, memory, oauth, pattern logs, staging, and provider cache writes.
Compact and context command plumbing
core/src/protocol.rs, core/src/provider.rs, core/src/main.rs, core/src/subagent.rs
The protocol, provider, and main core path now accept optional compaction instructions, emit compacting and context breakdown events, and thread the new command shapes through turn handling and subagent compaction.
Plugins, presence, and workspace activity
core/src/plugins.rs, core/src/presence.rs, core/src/workspace.rs, core/src/tools.rs, core/src/main.rs
Plugin manifests and runtime tool lists now carry disable, override, and system prompt data; the core also publishes workspace presence, exposes workspace_activity, and uses unconfined workspace resolution under Approval::Never.
Fetch host filtering
core/src/fetch_tool.rs
The fetch tool now treats IPv6 literals and private, loopback, and link-local addresses as blocked by default when the allowlist is empty, with matching denial messages and updated tests.

TUI and web command flow

Layer / File(s) Summary
Startup, caching, and output limits
tui/main.go, tui/mention.go, tui/blocks.go, tui/settings.go, tui/extras_test.go
The TUI adds watchdog-backed core startup, atomic process teardown, background mention cache refresh, capped tool output storage, and safer settings persistence.
Ask prompt flow
tui/ask.go, tui/handlers.go, tui/render.go, tui/ask_test.go
The ask flyout now parses blocking requests, routes keys into the prompt state, renders the overlay, and shows inline validation errors instead of transcript spam.
Context modal and compact command wiring
tui/handlers.go, tui/modal.go, tui/protocol.go, web/src/components/chat.tsx, web/src/lib/commands.ts, web/src/lib/reducer.ts, web/src/lib/types.ts, web/src/lib/use-agent.ts, tui/keybinds.go, tui/keybinds_test.go, tui/login_key_entry_test.go, tui/pin_test.go
The TUI adds context modal rendering, auto compact settings, compact instructions, and the matching web command and API updates.

Docs, skills, CI, and installer updates

Layer / File(s) Summary
Project documentation and skill guides
.catalyst-code/skills/*, README.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md
The README, contributing guide, code of conduct, and skill documents are rewritten or added to cover runner setup, review procedures, TTFT auditing, background tasks, and contributor workflow.
CI workflow runner and build changes
.github/workflows/ci.yml
The CI workflow moves build jobs to self-hosted Linux X64 runners, adds a gofmt check for TUI, and switches the Docker build job to buildah.
Windows installer flow
install.ps1
The PowerShell installer adds versioned download, checksum verification, web service installation, update, uninstall, state persistence, and help handling.

Estimated code review effort: 5 (Critical) | ~120 minutes


Note

🎁 Summarized by CodeRabbit Free

Your 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 @coderabbitai help to get the list of available commands.

karutoil added 8 commits July 7, 2026 21:16
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.
@karutoil
karutoil merged commit 2da1723 into master Jul 8, 2026
5 of 8 checks passed
@karutoil
karutoil deleted the ci/self-hosted-podman-runners branch July 8, 2026 20:22
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