Add Cloudflare Workers configuration#1
Open
cloudflare-workers-and-pages[bot] wants to merge 16 commits intomainfrom
Open
Add Cloudflare Workers configuration#1cloudflare-workers-and-pages[bot] wants to merge 16 commits intomainfrom
cloudflare-workers-and-pages[bot] wants to merge 16 commits intomainfrom
Conversation
Comprehensive reverse-engineered audit of every API, feature, and data flow. Server-side stack verified 100% working. Client-side tunnel has a macOS-specific environmental bug on hosts with pre-existing VPN interfaces. All bugs found during audit have been fixed or documented as follow-ups. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Proxies to DAM's agent restart endpoint via Provider. After editing user overlay configs (config.user.json for OpenClaw, config.user.yaml for Hermes), run 'tytus restart' to apply the changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pairs with Provider's new GET /pod/user-key and the Scalesys
user_stable_keys table. The user configures OPENAI_BASE_URL +
OPENAI_API_KEY in Cursor/Claude/etc exactly once, forever.
* pods/src/user_key.rs (new): get_user_key(&client) fetches the
user's stable pair from Provider /pod/user-key. Returns a typed
(endpoint, key) tuple. 404 maps to a friendly "run tytus connect
first" error.
* pods/src/request.rs: PodAllocation gains stable_ai_endpoint and
stable_user_key fields — returned by Scalesys via Provider on
every allocation so tytus-cli can cache them without a second
round-trip.
* cli/src/state.rs: PodEntry gains stable_ai_endpoint and
stable_user_key (both #[serde(default)] for migration from older
state.json files).
* cli/src/main.rs: cmd_env is now async and takes an HttpClient.
Default output (no flags) emits the stable pair:
OPENAI_BASE_URL=http://10.42.42.1:18080/v1
OPENAI_API_KEY=sk-tytus-user-<32hex>
plus TYTUS_API_KEY + TYTUS_AI_GATEWAY as aliases.
--raw emits the per-pod values (old behaviour, kept as an escape
hatch for debugging and backwards compat). Users who want
per-pod routing for specific tools can still use it.
If state has no stable_user_key cached (pre-Phase-2 state.json),
the command lazily fetches one via get_user_key and persists it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ytus
The metaphor: tytus-cli is a parasite, the AI agent (Claude Code,
OpenCode, KiloCode, Cursor, etc.) is the ant. Without rich docs the
ant has hands but no idea what levers exist. This commit hands over
the full lever map.
* New `llm-docs.md` at the workspace root — the canonical 320-line
reference for AI agents. Covers: what Tytus is, names/concepts,
plans + unit budgets, agents (nemoclaw=1u, hermes=2u), the FIXED
set of models on the gateway (ail-compound, ail-image, ail-embed,
minimax/ail-compound, minimax/ail-image — no others, no inventing),
the stable URL (10.42.42.1) + stable user key model, every
subcommand with its purpose, the seven MCP tools, five standard
recipes, an error catalog, hard rules, state/storage layout, and
what's intentionally not exposed.
* `tytus llm-docs` — new CLI subcommand that prints the reference.
Backed by `include_str!("../../llm-docs.md")` so the binary doesn't
need a separate file at runtime.
* `tytus_docs` — new MCP tool that returns the same content. Uses
the same include_str! source so the cli and mcp binaries can never
drift. Added to .mcp.json's alwaysAllow list when `tytus infect`
scaffolds a project so AI agents can call it without prompting.
* Rewrote CLAUDE_MD_BLOCK (Claude Code), AGENTS_MD_BLOCK (OpenCode /
Codex / Gemini), CLAUDE_COMMAND_TYTUS, KILO_COMMAND_TYTUS, and
ARCHON_COMMAND_TYTUS. Each one now points first at `tytus llm-docs`
as the source of truth, then summarizes the relevant subset for
the target tool (slash command body, AGENTS.md block, etc.).
The old constants referenced phantom models (qwen3-8b,
llama-3.1-8b-instruct, "383+ models") that don't exist on this
product — all replaced with the real five-model catalog.
* Rewrote every MCP tool description in mcp/src/main.rs:
- tytus_docs: cache the reference at session start
- tytus_status: always call first; explain the response shape
- tytus_env: document the stable vs raw distinction loudly
- tytus_models: list the actual five models in the description
- tytus_chat: constrain `model` to enum of valid ids;
explain MiniMax reasoning_content quirk re max_tokens
- tytus_revoke: mark DESTRUCTIVE, require user confirmation
- tytus_setup_guide: tell the agent to fall through to it on no-pod state
After running `tytus infect .` in a project, an AI agent like Claude
Code will see CLAUDE.md, AGENTS.md, .claude/commands/tytus.md,
.kilo/command/tytus.md, .archon/commands/tytus.md, .mcp.json, and
.tytus-env.sh — all with content that points at `tytus llm-docs` for
the full picture and contains enough inline guidance to bootstrap
any operation without further hand-holding.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three coordinated changes that make Tytus adoptable by any AI CLI on the
user's machine with a single copy-paste prompt — matching the 2md pattern.
=== .agents/skills/tytus/SKILL.md (new, hosted on GitHub) ===
YAML-frontmattered agent skill file at
.agents/skills/tytus/SKILL.md, mirroring traylinx/2md's
.agents/skills/2md/SKILL.md layout. Any AI tool can be bootstrapped via
a single URL fetch of the raw.githubusercontent.com link. Contents:
1. How to check if `tytus` is installed, and how to install it
(`curl -sSfL .../install.sh | sh`) if not.
2. How to read the full reference with `tytus llm-docs`.
3. How to branch on `tytus status --json` for setup flow.
4. The stable URL/key pair, plan tiers, the FIVE real models on the
pod gateway (ail-compound, ail-image, ail-embed, minimax/*), the
agent types (nemoclaw=1u, hermes=2u).
5. Five recipes (chat, use in local tool, switch agent, edit overlay,
link a project), error catalog, hard rules.
=== install.sh (rewritten) ===
Previous version relied on GitHub releases that don't exist yet and
would error out on `Error: Could not find release`. New version:
1. Detects OS + arch (macOS / Linux, x86_64 / aarch64).
2. Tries a prebuilt tarball download from the releases endpoint.
3. Falls back to `cargo install --git` if no release is published.
4. If cargo is missing, prompts (via /dev/tty so it works when piped
from curl) to install Rust via rustup.
5. Sets up passwordless sudoers for `tytus tunnel-up` so `tytus
connect` never prompts for a password (opt-out with
TYTUS_SKIP_SUDOERS=1).
6. Prints colored next-steps: `tytus setup`, `tytus connect`,
`tytus env --export`, `tytus link .`, `tytus bootstrap-prompt`,
`tytus llm-docs`.
Env vars: TYTUS_INSTALL_DIR, TYTUS_SKIP_SUDOERS, TYTUS_FORCE_SOURCE.
Idempotent — re-running upgrades in place.
=== tytus infect → tytus link ===
"Infect" was too negative for users. New primary name: `tytus link`
(link this project to Tytus). The old name remains as a hidden alias
(#[command(alias = "infect")]) so existing docs and muscle memory keep
working. User-facing output now says "Tytus linked into <dir>" instead
of "Tytus integration injected". JSON output key is "linked".
=== tytus bootstrap-prompt (new command) ===
Prints a short prompt mirroring 2md's "Read this URL and follow the
instructions" pattern. Hostname-stable reference to
raw.githubusercontent.com/traylinx/tytus-cli/main/.agents/skills/tytus/SKILL.md
plus a one-line install command. Users copy-paste it into any AI tool
to enable Tytus natively.
=== Docs updated ===
* llm-docs.md: infect→link in the command reference, noted the alias,
added a bootstrap-prompt entry.
* cli/src/main.rs: all slash-command bodies and status-hint messages
use `link` + `bootstrap-prompt` where appropriate.
Verified locally: `tytus link .` writes the full integration stack,
`tytus infect .` still works via alias, `tytus bootstrap-prompt`
prints the one-liner, `tytus llm-docs` prints the updated 330+ line
reference. install.sh passes both bash -n and sh -n syntax checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive security audit before flipping the repo to public so the
2md-style hosted-skill bootstrap pattern can use raw.githubusercontent.com.
Full report in docs/SECURITY-AUDIT.md. 13 findings across 7 phases; 1
CRITICAL, 3 HIGH, 3 MEDIUM, 6 LOW, 2 INFO. All blockers fixed.
=== CRITICAL — sudoers privilege escalation ===
The installer's sudoers entry included `/bin/kill -TERM *` which let any
local user SIGTERM ANY process as root (PID 1, system services, other
users' procs, security daemons). Real privilege escalation, not just bad
hygiene.
Fix: new hidden subcommand `tytus tunnel-down <pid>` that validates the
target PID against /tmp/tytus/tunnel-*.pid before signalling, refuses
PID <= 1, and verifies the process is alive (cleaning stale pidfiles).
The sudoers entry is now scoped to ONLY:
${USER} ALL=(root) NOPASSWD: ${BIN_PATH} tunnel-up *, ${BIN_PATH} tunnel-down *
cmd_disconnect now invokes `sudo -n <self> tunnel-down <pid>` instead
of `sudo -n kill -TERM <pid>`. The validation lives in the Rust binary
where it's tamper-evident, not in the sudoers grammar.
=== HIGH — README.md leaks + outdated content ===
Old README contained:
* `sk-566cecd...09a0` and `sk-c939e2...2318` — truncated display forms of
real production pod 01/02 keys (prefix+suffix exposed)
* `10.18.1.1`, `10.18.2.1` — production internal pod gateway IPs
* phantom models like `qwen3-8b` and "383+ models" (the real catalog has
five models: ail-compound, ail-image, ail-embed, minimax/ail-compound,
minimax/ail-image)
* a broken install URL: tytus.traylinx.com/install.sh doesn't exist
* "zombie fungus" / "parasitize" / "infect" wording
Full rewrite. Uses placeholder/stable values throughout
(http://10.42.42.1:18080/v1, sk-tytus-user-<32hex>), the accurate
five-model catalog, the correct raw.githubusercontent.com install URL,
the new positive `tytus link` verb, and a Security section that links
to the audit report.
=== HIGH — docs/VERIFICATION-2026-04-10.md is internal only ===
A 6.7KB engineering verification report committed under docs/. Contained
the production droplet IP (212.227.205.146), droplet ID (strato-eu-001),
exact resource specs, internal commit hashes from sibling private repos,
K8s deployment names, internal architecture details, and a "what's broken"
section. Never intended for public consumption.
Fix: file deleted from the working tree. (History rewrite is flagged as
operator-decision follow-up #2 in the audit report — the file remains in
git history until the operator decides whether to filter-repo it out
before flipping visibility.)
=== HIGH — docs/WIZARDS.md leaked internal IP ===
Used `http://10.18.1.1:18080` in a "Returning user" example. Replaced
with the stable `http://10.42.42.1:18080` and clarified "(stable, never
changes)".
=== MEDIUM — RUSTSEC-2026-0037 in quinn-proto 0.11.13 ===
Known high-severity vulnerability (CVSS 8.7) in the QUIC implementation
pulled in transitively via reqwest. Fixed by `cargo update -p quinn-proto`
to 0.11.14. Cargo.lock committed.
=== MEDIUM — CLAUDE.md outdated ===
Engineering CLAUDE.md still said `tytus infect`, missed the new
link/bootstrap-prompt/llm-docs/tunnel-down commands, and had stale
architecture notes. Rewritten to reflect current state, hidden
subcommands, security invariants, the stable URL/key model, and
contributing guidelines.
=== MEDIUM — broken install URL in mcp/src/tools.rs ===
The tytus_setup_guide MCP tool returned a step pointing at the dead
tytus.traylinx.com/install.sh URL. Replaced with the correct
raw.githubusercontent.com path. Also removed the now-incorrect "requires
sudo for TUN device" wording (the elevation chain handles it internally).
=== LOW — .gitignore expanded ===
Was just target/, *.swp, .DS_Store. Added explicit blocks for .env*,
*.pem, *.key, *.p12, *.pfx, *.crt, secrets/, state.json, **/state.json,
*.log, logs/, .idea/, .vscode/, *.iml, .cache/, with !.env.example to
allow committing example templates.
=== LOW — clippy clean ===
23 warnings (style, dead-code, needless borrow, map_or → is_none_or,
match → matches!). Auto-fixed the trivial ones via `cargo clippy --fix`,
hand-fixed the four that needed annotations:
- auth: #[allow(dead_code)] on WannolotPassResponse with explanation
- pods: #[allow(dead_code)] on post_with_retry with API-symmetry note
- tunnel/monitor: rewrote match → matches!
- cli: removed misplaced #[allow(dead_code)] above CLAUDE_MD_BLOCK
Result: `cargo clippy --workspace --all-targets` returns ZERO warnings.
=== LOW — Cargo.toml metadata ===
Added description, homepage, repository, documentation, readme, keywords,
categories, rust-version. Crate is now ready for `cargo publish` if/when
we want it on crates.io.
=== LOW — Source comments sanitized ===
tunnel/src/lib.rs and tunnel/src/monitor.rs comments used concrete past
production droplet octets (10.17.8.X, 10.18.1.X) as examples. Replaced
with placeholder format (10.X.Y.Z) plus a note that the stable address
10.42.42.1 is now appended to AllowedIPs by the bootstrap.
=== Findings NOT requiring code changes ===
* Hardcoded production HTTPS endpoints (tytus.traylinx.com,
api.makakoo.com, sentinel.traylinx.com) are public SaaS endpoints — they
WILL appear in strings(1) output of any compiled binary regardless of
storage form. Including them in source is correct for a SaaS client.
* Keychain service name `com.traylinx.atomek` (legacy codename) — changing
it would invalidate existing user keychain entries. Documented as
do-not-touch in CLAUDE.md.
* fxhash and number_prefix unmaintained-crate warnings — neither is a
vulnerability. Tracked for next dependency sweep.
=== Verification gate (all green) ===
* `cargo build --release -p atomek-cli -p tytus-mcp` — clean
* `cargo clippy --workspace --all-targets` — 0 warnings
* `cargo audit` — 0 vulnerabilities, 2 unmaintained-crate warnings
* `cargo test --workspace` — passes (0 tests; backlog item LOW-3)
* `sh -n install.sh && bash -n install.sh` — both clean
* `tytus tunnel-down 1` — refuses PID 1 with clear error
* `tytus tunnel-down <random>` — refuses unknown PID
* README has no truncated key fingerprints
* README has no internal IPs
* docs/VERIFICATION-*.md removed
=== Operator sign-off ===
After reviewing docs/SECURITY-AUDIT.md, the operator can flip the repo:
gh repo edit traylinx/tytus-cli --visibility public \
--accept-visibility-change-consequences
The audit report's "Operator sign-off" section lists the post-flip
verification steps (test the bootstrap prompt with a fresh install,
cut v0.1.0 release, etc).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-flip-to-public secret sweep caught two illustrative examples that happened to match real production internal IPs: * llm-docs.md §5: the "tytus env --raw" example used http://10.18.2.1:18080 and sk-c939e21c... (matches pod 02 on the live droplet). Replaced with placeholder format http://10.X.Y.1:18080 + sk-<48 hex> and clarified these change on pod rotation, droplet migration, or octet reassignment. * tunnel/src/wireguard.rs:133 doc comment used "10.18.1.0/24" as the AllowedIPs format example. Replaced with "10.X.Y.0/24". After this commit, the only file in the repo that still contains the documented leak strings is docs/SECURITY-AUDIT.md itself — that is by design, since a security audit must describe what it found. Verified via: grep -rnE 'sk-[a-f0-9]{40,}|212\.227\.205\.146|sk-566cecd|sk-c939e2|10\.18\.[0-9]+\.[0-9]+|10\.17\.[0-9]+\.[0-9]+' \ --include='*.rs' --include='*.sh' --include='*.md' --include='*.toml' \ --include='*.json' --include='*.yml' --include='*.yaml' . \ | grep -v docs/SECURITY-AUDIT.md # → empty Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… + ui
Sprint doc: docs/sprints/SPRINT-TYTUS-PAYING-CUSTOMER-READY.md (in ProjectWannolot).
Mandate: a paying customer with only their subscription and tytus CLI must go
zero-to-working-pod with zero errors. Triggered by live debug session where
every failure mode below was hit.
FIX-1 wg keepalive + handshake watchdog (tunnel/src/wireguard.rs)
Force persistent_keepalive=25 regardless of server config. Add 90s idle-RX
watchdog that re-initiates handshake via format_handshake_initiation(force=true)
throttled at 15s. Upgrade silently-swallowed debug! on encap/decap/UDP/TUN
errors to warn! so real failures show at default log level. Solves the
"tunnel silently dies after ~20min of idle" split-brain.
FIX-2 disconnect reaps from pidfile (cli/src/tunnel_reap.rs + disconnect handler)
Pidfile at /tmp/tytus/tunnel-NN.pid is authoritative, not state.json.tunnel_iface
(which gets cleared on revoke, leaving disconnect blind). New shared module
with ReapOutcome { Reaped, NoPidfile, StalePidfile, ReapFailed }, uses scoped
sudoers 'tytus tunnel-down <pid>' helper — no password prompt. 500ms SIGTERM
grace. 12 integration tests.
FIX-3 revoke reaps before wiping (revoke handler)
Revoke now calls reap_tunnel_for_pod() before the Provider API call + state
wipe. Invariant: after revoke returns, zero orphan processes. Revoke still
completes if reap fails — user explicitly asked to destroy. 3 integration tests.
FIX-4 packet-loop exit detection (cmd_tunnel_up)
tokio::select! on ctrl_c() vs. packet-loop JoinHandle so unexpected task
completion logs FATAL to /tmp/tytus/tunnel-NN.log and exits code 2 instead
of sitting there pretending to be alive while utun was dropped. Adds
TunnelHandle::cancel_token() + take_task() to atomek-tunnel.
FIX-5 THE BIG ONE — daemon detach (cmd_tunnel_up)
Daemon died 3-4 minutes after every tytus connect. Root cause: stdout/stderr
piped back to parent tytus connect so parent could read TUNNEL_READY. Once
parent exited, pipes closed, the first subsequent write (keepalive tick,
tracing warn!, watchdog log) hit SIGPIPE → default handler killed daemon.
Plus: daemon inherited parent's session so closing terminal sent SIGHUP to
whole session. Fix: libc::setsid() for own session, signal(SIGHUP, SIG_IGN),
signal(SIGPIPE, SIG_IGN), dup2(/dev/null, {0,1,2}) after flushing TUNNEL_READY.
Without this, every real user closing their terminal would kill their tunnel.
Verified: daemon survived 6m37s+ past previous death zone.
FIX-6 autostart on boot (new subcommand: tytus autostart install|uninstall|status)
macOS: writes ~/Library/LaunchAgents/com.traylinx.tytus.plist + launchctl load -w.
Linux: writes ~/.config/systemd/user/tytus.service + systemctl --user enable --now.
After reboot, the LaunchAgent/unit runs 'tytus connect' at login, reuses
the stable pair (http://10.42.42.1:18080 + sk-tytus-user-*) from state.json.
User's apps keep working across reboots with zero reconfig — the
"like Ollama" experience.
tytus ui — new subcommand for OpenClaw Control UI access
Browsers refuse WebCrypto / device-identity APIs on non-localhost HTTP.
Direct http://10.18.X.1:3000 gets the "control ui requires device identity"
banner. tytus ui starts a 127.0.0.1:3000 TCP forwarder to the pod's agent
port via tokio::io::copy_bidirectional, opens the default browser, blocks
on Ctrl+C. --port override for conflicts, auto-fallback to next 5 ports,
--no-open for headless.
Test suite: 24/24 green (tunnel_reap unit 9 + disconnect_pidfile 12 + revoke_reaps_daemon 3).
cargo build --release --workspace clean. cargo clippy --workspace --all-targets -- -D warnings clean.
Validator lane: opencode PASS on FIX-1/2/3. gemini-cli second opinion PASS on
FIX-2/3 merge strategy. FIX-4/5/6 shipped inline during live diagnosis and
pending post-commit opencode review.
Known follow-up: cleanup_files in tunnel_reap.rs silently fails on root-owned
stale pidfiles because disconnect runs as user. Fix in a follow-up sprint by
routing deletion through the scoped tunnel-down helper or daemon-side 0666 perms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ctural audit Sprint 1: Headless Token Refresh & Startup Reliability - ensure_token() returns Result<()> — all 10 callers handle errors explicitly (F2) - Headless detection: --headless flag + TYTUS_HEADLESS=1 env var (F3) - Proactive token refresh in 5-10min window before expiry (F4) - Server-side token validation via GET /oauth/token/info (F1) - Structured diagnostic log to /tmp/tytus/autostart.log (F7) - save_critical() for token rotation — prevents RT loss on disk write failure - SIGTERM handler in tunnel daemon — prevents silent tunnel death - Stale tunnel detection in tytus status via reap_dead_tunnels() - LaunchAgent plist updated with TYTUS_HEADLESS=1 Sprint 2: Daemon Skeleton (Phase 1) - New daemon.rs module: Unix socket server at /tmp/tytus/daemon.sock - JSON-line protocol: ping, status, refresh, shutdown commands - Background token refresh loop (5-minute interval) - CLI subcommands: tytus daemon run/stop/status - Graceful shutdown: SIGTERM + SIGINT via tokio::signal Sprint 3: System Tray (tytus-tray) - New tray/ crate: menu bar icon for macOS (tray-icon + objc2-app-kit) - Dynamic "Open in" submenu: detects installed AI CLIs on PATH - Launch flow: write temp script → tytus link --only <filter> → export env vars → launch CLI - CLI mapping: Claude→claude, OpenCode→opencode, Gemini/Codex→agents, Aider→shell - Self-deleting temp script (no API keys persist on disk) - Terminal detection: iTerm2 > Terminal.app Documentation: llm-docs.md, CLAUDE.md updated with new features. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ting Six user-facing guides in docs/guides/: - Getting Started: install → setup → first chat in 2 minutes - Use with AI Tools: Claude Code, Cursor, OpenCode, Gemini, Aider, Vibe - Plans, Agents, and Models: tiers, unit budgets, model catalog - Auto-Start and Daemon: survive reboots, tray icon, background refresh - Common Use Cases: copy-paste recipes for real scenarios (Python, curl, CI/CD, image generation, embeddings, team sharing) - Troubleshooting: fix common issues in 30 seconds, tytus doctor guide All guides are use-case centric, not developer-oriented. Written for end users who want to set up their pod and start coding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CRITICAL SECURITY FIX: tytus status/connect no longer exposes: - droplet_id (infrastructure naming → hosting provider identification) - droplet_ip (public IP → enables direct SSH brute-force) - ai_endpoint (internal 10.18.x.y IPs → pod subnet mapping) - agent_endpoint (internal agent port → attack surface) - pod_api_key (raw per-pod key → credential sprawl risk) User-facing output now shows ONLY: - pod_id, agent_type, tunnel_iface (operational) - stable_ai_endpoint (10.42.42.1:18080 — designed for exposure) - stable_user_key (masked in human output, full in JSON) Infrastructure details still accessible via `tytus env --raw` for debugging (explicit opt-in). Full security audit documented in docs/SECURITY-HARDENING-2026-04-12.md: - Network scan: cross-pod isolation verified (PASS) - Metadata API blocked (PASS) - K8s not reachable through tunnel (PASS) - Droplet SSH open on public internet (CRITICAL — flagged for infra team) - /metrics endpoint unauthenticated (MEDIUM — flagged for infra team) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full reverse-engineering security audit by 3 parallel auditors with independent review by OpenCode (MiniMax) and Gemini CLI. CRITICAL (1): - C1: install.sh downloads binary with no checksum + grants passwordless root via sudoers wildcard — unauthenticated path to root HIGH (5): - H1: Hardcoded API key in binary (extractable via strings) - H2: Refresh token in plaintext state.json (contradicts security docs) - H3: Sudoers wildcard "tunnel-up *" allows arbitrary file read as root - H4: WG private key in predictable temp file with race window - H5: MCP tytus_env returns raw keys instead of stable values MEDIUM (12), LOW (8), INFO (8) — see docs/SECURITY-DEEP-AUDIT-2026-04-12.md Team consensus: C1 is launch-blocking. H1-H5 must fix before launch. Network isolation (cross-pod, metadata, K8s) verified PASS. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Active exploitation of every finding from the deep audit. Each rated EXPLOITED (proven) or MITIGATED (proven). EXPLOITED (proven with live exploit): - E1 (10/10): /bin/kill -TERM * in sudoers — any process can kill ANY system process as root including PID 1. IMMEDIATE fix needed. - E2 (9/10): refresh_token in plaintext state.json — stolen and used to get new access_token. Full account takeover from one file. - E3 (8/10): MCP tytus_env returns raw per-pod API key + internal IPs to AI agents. Never updated when CLI was fixed. - E4 (4/10): Daemon socket status response leaks ai_endpoint (internal IP) - E5 (6/10): /tmp/tytus/ directory is 744 (world-readable) MITIGATED (proven safe): - CLI status output (fixed this session) - Cross-pod isolation (verified by network scan) - DNS through tunnel (no internal resolution) - Subnet isolation (docker bridge, host network unreachable) - Hardcoded key not trivially extractable (partially mitigated) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rew tap Grandma-grade public install surface: curl -fsSL https://tytus.traylinx.com/install.sh | bash powershell -c "irm https://tytus.traylinx.com/install.ps1 | iex" brew install traylinx/tap/tytus Security (from pentest C1): - install.sh now downloads SHA256SUMS and verifies before extracting; refuses to install if the sums file is missing or a mismatch is detected. Escape hatch: TYTUS_SKIP_CHECKSUM=1 (not recommended). - Sudoers wildcard tightened from `tunnel-up *` to `tunnel-up /tmp/tytus/tunnel-*.json` so an attacker can no longer point tunnel-up at arbitrary files. tunnel-down helper already validates PIDs. - release.yml generates SHA256SUMS across all artifacts so the verification path actually has something to verify against. New files: - install.ps1 Windows installer (release path + cargo fallback) - web/index.html single-file landing page (no JS frameworks) - web/build.sh Cloudflare Pages build step - web/_redirects routing for future expansion - contrib/homebrew/tytus.rb formula template with {{VERSION}} + SHAs - .github/workflows/homebrew.yml auto-publish to traylinx/homebrew-tap - docs/PUBLISHING.md end-to-end release + infra runbook Release workflow changes: - Added aarch64-unknown-linux-gnu target via cross - Flattens artifacts into dist/ then emits SHA256SUMS - Attaches SHA256SUMS to the release so install.sh can fetch it Note: Windows tunnel support is still experimental (wintun.dll not bundled yet). install.ps1 prints a clear warning. Login/chat/env/link/mcp all work. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes every exploited finding from docs/PENTEST-RESULTS-2026-04-12.md except the infra-team items (SSH on droplet, /metrics auth). E2/H2 refresh_token no longer in state.json E3/H5 MCP tytus_env returns stable values by default E4 daemon socket status drops ai_endpoint E5 /tmp/tytus/ is 0700 and files inside are 0600 H1 hardcoded Api-Key formally documented as public client ID # E2/H2 — refresh_token out of state.json `cli/src/state.rs` and `mcp/src/state.rs` now mark refresh_token as `#[serde(default, skip_serializing)]`. Load() reads it from the OS keychain on every call, so the rest of the CLI continues to work unchanged. Legacy state.json files that still contain a refresh_token are migrated eagerly in load(): the value is copied into the keychain, then save_critical() rewrites the file immediately without the field. This closes the window where a failed downstream call would leave plaintext tokens on disk. CRITICAL FIX: `keyring = "3"` ships with NO backends by default — the default bundle is a stub that silently accepts writes and never persists. Enabled features: apple-native, linux-native-sync-persistent, sync-secret-service, windows-native. Before this fix, every previous release that relied on "store in keychain" was actually a no-op. Verified end-to-end: legacy state.json with RT is now migrated into the real macOS login keychain, and subsequent reads load it back. # E3/H5 — MCP stable-by-default mcp/src/tools.rs: tytus_env now returns stable_ai_endpoint + stable_user_key by default. Added `raw` boolean parameter for debug mode (legacy per-pod values). tytus_status no longer leaks ai_endpoint or agent_endpoint. tytus_models and tytus_chat prefer stable values and fall back to per-pod for robustness. mcp/src/main.rs schema updated. # E4 — daemon status redaction cli/src/daemon.rs: removed ai_endpoint from the `status` socket response. Now emits only pod_id, agent_type, tunnel_iface, stable_ai_endpoint, stable_user_key. The CLI's print_*_status() already redacts the same way; the daemon must not leak more. # E5 — /tmp/tytus/ permissions Added `secure_tytus_tmp_dir()` + `secure_chmod_600()` helpers in main.rs and wired every /tmp/tytus/ create/write site: tunnel-up (runs as root), daemon socket + pidfile, autostart.log, tray launcher script, connect's pid+iface files. Directory is 0700, every file inside is 0600. The live machine's existing files were also repermissioned. # H1 — hardcoded Api-Key decision Consulted opencode (MiniMax) + gemini CLI. Weight of evidence: this is a public client identifier, not a secret. Every endpoint that uses it also requires user credentials. Documented as PUBLIC_CLIENT_API_KEY in auth/src/login.rs and auth/src/sentinel.rs with a long comment explaining the threat model. New docs/SECURITY.md captures the full security model including why this is shipped intentionally (same pattern as Firebase API keys, Auth0 client_id, Stripe publishable keys). Not rotatable without breaking every installed binary — if Rails ever adds an endpoint that trusts Api-Key alone, this ceases to be safe and must be caught in Rails review, not CLI review. # Testing - cargo check --workspace: clean - cargo clippy --workspace: clean (one dead_code warning fixed) - cargo test --workspace: 24 tests passed - End-to-end migration test: legacy state.json → keychain + stripped file - /tmp/tytus perms verified: 0700 dir, 0600 files - Daemon status response verified redacted - MCP tytus_env verified stable-by-default Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
tytus-cli | 4bcaaf9 | Commit Preview URL Branch Preview URL |
Apr 12 2026, 10:19 PM |
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.
This PR configures your project for Cloudflare Workers deployment using Wrangler autoconfig.
Merging this PR commits the configuration to your repository, enabling faster deployments and version controlled settings.
Detected settings:
Framework:
staticDeploy command:
npx wrangler deployVersion (non-production deploy) command:
npx wrangler versions uploadNext steps after merging:
Your Worker configuration lives in
wrangler.jsonc. You can now:View build details · Join the discussion for questions or feedback