Agentic Coding Flywheel Setup (ACFS)
🌐 agent-flywheel.com — Interactive setup wizard for beginners
From zero to fully-configured agentic coding VPS in 30 minutes. A complete bootstrapping system that transforms a fresh Ubuntu or Arch-based machine into a professional AI-powered development environment.
|
The Vision Beginner with laptop → Wizard → VPS → Agents coding for you |
Quick Install
{ acfs_installer="$(curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh")" || acfs_installer="$(curl -fsSL "https://cdn.jsdelivr.net/gh/Dicklesworthstone/agentic_coding_flywheel_setup@main/install.sh")"; } && printf '%s\n' "$acfs_installer" | bash -s -- --yes --mode vibeNOTE
raw.githubusercontent.comis listed first deliberately. jsDelivr caches a mutable@mainreference, so it can serve an installer that is hours or days behind the repository. When that happens the stale installer's bootstrap extraction list does not match the current checksum ledger and the install fails withINTEGRITY: <file> missing— a failure that looks like a repository defect but is purely a CDN cache artifact. Keeping the authoritative source primary and the CDN as fallback preserves the resilience without that failure mode. Please do not reorder these. Each download is buffered before execution so a failed request cannot concatenate a partial installer with the fallback response.
The installer is idempotent—if interrupted, simply re-run it. Resume revisits installation phases and uses component checks to repair incomplete installations.
Using a cloud agent instead of a VPS? The cloud agent guide has tested Claude Code / Codex setups and researched recipes for other Linux agents. See cloud environments.
Production environments: For stable, reproducible installs, pin to a tagged release or specific commit:
# Preferred: use a tagged release (e.g., v0.9.0) curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/v0.9.0/install.sh" | bash -s -- --yes --mode vibe --ref v0.9.0 # Alternative: pin to a specific commit SHA curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/abc1234/install.sh" | bash -s -- --yes --mode vibe --ref abc1234Tagged releases are tested and stable. Passing
--refensures all fetched scripts use the same version.
TL;DR
ACFS is a complete system for bootstrapping agentic coding environments:
Why you'd care:
- Zero to Hero: Takes complete beginners from "I have a laptop" to "I have Claude/Codex/Antigravity agents writing code for me on a VPS"
- One-Liner Magic: A single
curl | bashcommand installs 30+ tools, configures everything, and sets up three AI coding agents - Vibe Mode: Pre-configured for maximum velocity—passwordless sudo, dangerous agent flags enabled, optimized shell environment
- Battle-Tested Stack: Includes the complete Agent Flywheel stack (core tools + utilities) for agent orchestration, coordination, and safety
What you get:
- Modern shell (zsh + oh-my-zsh + powerlevel10k)
- All language runtimes (bun, uv/Python, Rust, Go)
- Agent coordination tools (NTM, MCP Agent Mail, SLB)
- Cloud CLIs (Vault, Wrangler, Supabase, Vercel)
- And 20+ more developer tools
Coding agents: 7 modules — 3 installed by default (Antigravity CLI, Claude Code, Codex CLI); 3 optional (Grok CLI, oh-my-pi, OpenCode); 1 legacy, off by default (Gemini CLI). Full roster in Compatible AI Coding Agents.
The ACFS Experience
graph LR
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#e8f5e9', 'lineColor': '#90a4ae'}}}%%
subgraph user ["User's Machine"]
LAPTOP["Laptop"]
BROWSER["Browser"]
end
subgraph wizard ["Wizard Website"]
STEPS["13-Step Guide"]
end
subgraph vps ["Fresh VPS"]
UBUNTU["Ubuntu 22.04 / 24.04 LTS"]
INSTALLER["install.sh"]
CONFIGURED["Configured VPS"]
end
subgraph agents ["AI Agents"]
CLAUDE["Claude Code"]
CODEX["Codex CLI"]
AGY["Antigravity CLI"]
end
LAPTOP --> BROWSER
BROWSER --> STEPS
STEPS -->|SSH| UBUNTU
UBUNTU --> INSTALLER
INSTALLER --> CONFIGURED
CONFIGURED --> CLAUDE
CONFIGURED --> CODEX
CONFIGURED --> AGY
classDef user fill:#e3f2fd,stroke:#90caf9,stroke-width:2px
classDef wizard fill:#fff8e1,stroke:#ffcc80,stroke-width:2px
classDef vps fill:#f3e5f5,stroke:#ce93d8,stroke-width:2px
classDef agent fill:#e8f5e9,stroke:#a5d6a7,stroke-width:2px
class LAPTOP,BROWSER user
class STEPS wizard
class UBUNTU,INSTALLER,CONFIGURED vps
class CLAUDE,CODEX,AGY agentFor Beginners
ACFS includes a step-by-step wizard website at agent-flywheel.com that guides complete beginners through:
- Installing a terminal on their local machine
- Generating SSH keys (for secure access later)
- Renting a VPS from providers like OVH or Contabo
- Connecting via SSH with a password (initial setup)
- Running the installer (which sets up key-based access)
- Reconnecting securely with your SSH key
- Starting to code with AI agents
For Developers
ACFS is a one-liner that transforms any fresh Ubuntu or Arch-based machine into a fully-configured development environment with modern tooling and three AI coding agents ready to go.
For Teams
ACFS provides a reproducible, idempotent setup that ensures every team member's VPS environment is identical—eliminating "works on my machine" for agentic workflows.
Architecture & Design
ACFS centers its tool inventory and generated module metadata on the manifest.
Generated installer libraries and doctor checks derive from it, while
install.sh and several orchestration libraries retain explicit authored
handoffs. Drift checks cover the seam between those two layers.
One-Page System Data Flow
flowchart TB
%% User and website
subgraph U["User (local machine)"]
Browser["Browser"]
Terminal["Terminal / SSH client"]
end
subgraph W["Wizard Website (Next.js 16) — apps/web"]
Wizard["Wizard UI (/wizard/*)"]
InstallRoute["GET /install (302 redirect to raw install.sh)"]
WebState["State: URL params + localStorage"]
end
%% Repo sources
subgraph R["Repo (source)"]
Manifest["acfs.manifest.yaml<br/>Modules + install + verify + deps"]
Generator["packages/manifest<br/>Parser (Zod) + generate.ts"]
Generated["scripts/generated/*<br/>source-only libraries/harness + doctor/index/checksum data"]
Installer["install.sh (production one-liner)"]
Lib["scripts/lib/*<br/>security / doctor / update / services-setup"]
Configs["acfs/*<br/>zshrc + tmux.conf + onboard lessons"]
Checksums["checksums.yaml<br/>sha256 for upstream installers"]
Tests["tests/vm/test_install_ubuntu.sh<br/>Docker integration test"]
end
%% Target VPS
subgraph V["Target VPS (Ubuntu LTS, existing release preserved)"]
Run["Run install.sh"]
Verify["Verified upstream installers<br/>(security.sh + checksums.yaml)"]
AcfsHome["~/.acfs/<br/>configs + scripts + state.json"]
Commands["Commands<br/>acfs doctor / acfs update / acfs services / acfs services-setup / onboard"]
Tools["Installed tools<br/>bun/uv/rust/go + tmux/rg/gh + vault + ..."]
Agents["Agent CLIs<br/>claude / codex / agy"]
Stack["Stack tools<br/>ntm / mcp_agent_mail / ubs / bv / cass / cm / caam / slb / dcg / ru"]
end
%% Website guidance flow
Browser --> Wizard
Wizard --> WebState
Wizard --> InstallRoute
InstallRoute -->|redirects to| Installer
%% How users fetch/run the installer
Terminal -->|curl / bash| Installer
Terminal -->|SSH| Run
%% Manifest-driven generation
Manifest --> Generator --> Generated
Generated -->|install.sh sources category libraries<br/>and dispatches private module functions| Installer
%% Installer composition
Lib --> Installer
Configs --> Installer
Checksums --> Installer
Tests -->|validates| Installer
%% VPS install results
Installer --> Run
Run --> Verify
Verify --> Tools
Verify --> Agents
Verify --> Stack
Run --> AcfsHome --> Commands┌─────────────────────────────────────────────────────────────────────────────┐
│ SOURCE OF TRUTH │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ acfs.manifest.yaml │ │
│ │ Tool Definitions • Install Commands • Verification Logic │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────┴─────────────────┐
▼ ▼
┌───────────────────────────────────┐ ┌───────────────────────────────────┐
│ CODE GENERATION │ │ WIZARD WEBSITE │
│ ┌─────────────────────────────┐ │ │ ┌─────────────────────────────┐ │
│ │ TypeScript Parser (Zod) │ │ │ │ apps/web/ (Next.js 16) │ │
│ │ generate.ts │ │ │ │ agent-flywheel.com │ │
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
└───────────────────────────────────┘ └───────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────────┐
│ GENERATED OUTPUTS (REFERENCE) │
│ scripts/generated/: 13 source-only category libraries + install_all │
│ harness + doctor checks + manifest index + schema-1 checksum ledger │
└───────────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────────┐
│ INSTALLER │
│ install.sh + scripts/lib/*.sh + checksums.yaml (SHA256 verification) │
│ (category libraries are sourced; install.sh owns production dispatch) │
└───────────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────────┐
│ TARGET VPS │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 30+ Tools │ │ zsh + p10k │ │ AI Agents │ │ ~/.acfs/ │ │
│ │ Installed │ │ Shell Config │ │ Claude/Codex │ │ Configurations│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
└───────────────────────────────────────────────────────────────────────────┘Why This Architecture?
Single Source of Truth: The manifest file (acfs.manifest.yaml) defines every tool—its name, description, install commands, and verification logic. When you add or edit a tool in the manifest, the generator automatically updates the generated scripts and manifest-derived checks. The production one-liner installer (install.sh) is still hand-written today, so behavior changes may also require updating install.sh until full migration.
TypeScript + Zod Validation: The manifest parser uses Zod schemas to validate the YAML at parse time. Typos, missing fields, and structural errors are caught immediately during generation—not at runtime on a user's VPS when the installer fails halfway through.
Generated Scripts: Rather than hand-maintaining one installer script per manifest category and keeping them synchronized, the generator produces them from the manifest. This means:
- A consistent, auditable view of manifest-defined install logic (some modules intentionally emit TODOs)
- Consistent error handling and logging across all modules
- A clear path toward future installer integration
Components
| Component | Path | Technology | Purpose |
|---|---|---|---|
| Manifest | acfs.manifest.yaml | YAML | Single source of truth for all tools |
| Generator | packages/manifest/src/generate.ts | TypeScript/Bun | Produces installer scripts from manifest |
| Website | apps/web/ | Next.js 16 + Tailwind 4 | Step-by-step wizard for beginners |
| Installer | install.sh | Bash | One-liner bootstrap script |
| Lib Scripts | scripts/lib/ | Bash | Modular installer functions |
| Generated Scripts | scripts/generated/ | Bash/data | Source-only category libraries and harness, doctor checks, manifest metadata, and the schema-1 internal checksum ledger |
| Configs | acfs/ | Shell/Tmux configs | Files deployed to ~/.acfs/ |
| Onboarding | acfs/onboard/ | Bash + Markdown | Interactive tutorial system |
| Checksums | checksums.yaml | YAML | SHA256 hashes for upstream installers |
The Manifest System
acfs.manifest.yaml is the canonical inventory for generated ACFS modules. It
defines their installation and verification metadata; authored orchestration
remains responsible for bootstrap, lifecycle, and registered non-generated
handoffs.
Manifest Structure
version: 2
name: agentic_coding_flywheel_setup
id: acfs
defaults:
user: ubuntu
workspace_root: /data/projects
mode: vibe
modules:
- id: base.system
description: Base packages and sane defaults
category: base
run_as: root
optional: false
enabled_by_default: true
generated: true
phase: 1
install:
- apt-get update -y
- apt-get install -y curl git ca-certificates unzip tar xz-utils jq build-essential
verify:
- curl --version
- git --version
- jq --versionEach module specifies:
- description: Human-readable name
- category: One of the canonical groups: base, users, filesystem, shell, cli, network, lang, tools, db, cloud, agents, stack, acfs
- install: Commands to run (or descriptions that become TODOs)
- verify: Commands that must succeed to confirm installation
The Generator Pipeline
The TypeScript generator (packages/manifest/src/generate.ts) reads the manifest and produces:
-
Category Libraries (
scripts/generated/install_base.sh,install_agents.sh, etc.)- One source-only file per category with private module functions
- Consistent logging and error handling
- Verification checks after each module
- Direct execution fails closed because a single category cannot satisfy cross-category dependencies
-
Doctor Checks (
scripts/generated/doctor_checks.sh)- All verify commands extracted into a runnable health check
- Tab-delimited format (to safely handle
||in shell commands) - Reports pass/fail/skip for each module
-
Generated Harness (
scripts/generated/install_all.sh)- Sources all category libraries
- Invokes private module functions in global dependency order
- Sourceable generated-module harness; direct execution refuses because it omits orchestration-owned modules and production dispatcher semantics
-
Runtime Data (
scripts/generated/manifest_index.sh,internal_checksums.sh)- Deterministic module/category/generated-ownership metadata
- Closed-grammar schema-1 digests for checksum-controlled runtime scripts
Note: Production
install.shis the only supported installation entry point. It sources the category libraries and dispatches their private functions through its phase runner; it does not callinstall_all.sh.
To regenerate after manifest changes:
cd packages/manifest
bun run generate # Generate scripts
bun run generate:dry # Preview without writingWhy TypeScript for Code Generation?
Shell can parse YAML with yq, but TypeScript + Zod offers:
- Type safety: The parser knows the exact shape of a manifest
- Validation: Zod catches malformed YAML with descriptive errors
- Transformation: Complex logic (sorting by dependencies, escaping) is natural in TypeScript
- Consistency: All generated code follows the same patterns
The generator centralizes the substantially larger Bash output in one reviewed TypeScript implementation. Exact line and file counts are intentionally derived from the current manifest rather than frozen in this document.
Security Verification
ACFS downloads and executes installer scripts from the internet. This is inherently risky—a compromised upstream could inject malicious code. The security verification system mitigates this risk.
How It Works
The checksums.yaml file contains SHA256 hashes for all upstream installer scripts:
# checksums.yaml
installers:
bun:
url: "https://bun.sh/install"
sha256: "a1b2c3d4..."
rust:
url: "https://sh.rustup.rs"
sha256: "e5f6a7b8..."The security library (scripts/lib/security.sh) provides:
-
HTTPS Enforcement: All installer URLs must use HTTPS. Non-HTTPS URLs fail immediately.
-
Checksum Verification: Before executing a downloaded script, the system:
- Downloads the content to memory
- Calculates the SHA256 hash
- Compares against the stored hash
- Only executes if they match
-
Verification Modes:
./scripts/lib/security.sh --print # List all upstream URLs ./scripts/lib/security.sh --verify # Verify all against saved checksums ./scripts/lib/security.sh --update-checksums # Generate new checksums.yaml ./scripts/lib/security.sh --checksum URL # Calculate SHA256 of any URL
The uv installer also requires a working sha256sum from coreutils in its
clean target-user environment. Immediately before executing uv's verified
installer, ACFS checks the verifier with a known SHA256 result using the same
system PATH and account. A missing, failing, or incorrect verifier stops the
installation before uv can download release archives, and asks you to repair
or install coreutils. This applies to both
the legacy and generated installers and preserves the installer's stdin.
When Checksums Fail
A checksum mismatch can mean:
- Normal update: The upstream maintainer released a new version
- Potential compromise: Someone modified the script maliciously
The verification report distinguishes these cases:
- If multiple checksums fail simultaneously, investigate before updating
- If a single checksum fails after a known release, update is likely safe
To update after verifying a legitimate upstream change:
# Write to a candidate file first: redirecting straight onto checksums.yaml
# truncates it before the script runs, so a single fetch error would leave
# it empty and every verified installer would fail closed.
./scripts/lib/security.sh --update-checksums > /tmp/acfs-checksums.candidate.yaml
diff -u checksums.yaml /tmp/acfs-checksums.candidate.yaml # Review what changed
cp /tmp/acfs-checksums.candidate.yaml checksums.yaml
git commit -m "chore: update upstream checksums"Why This Approach?
The curl | bash pattern is controversial but practical. ACFS makes it safer by:
- Verifying content before execution (not just transport via HTTPS)
- Making checksums auditable in version control
- Providing tools to detect and investigate changes
- Failing closed (no execution on mismatch)
This is defense in depth—HTTPS protects transport, checksums protect content.
Omarchy (Arch) support
Omarchy (and Arch Linux generally) is supported by the same one-liner — no separate command required. The installer auto-detects your distribution and adapts:
- pacman instead of apt: system packages and CLI tools install via
pacman. - Your existing starship config is preserved: if you already have a customized
starship.toml, ACFS leaves it alone rather than overwriting it. - No oh-my-zsh / powerlevel10k on Arch: Arch users typically have an opinionated shell setup already, so the installer skips the oh-my-zsh + powerlevel10k phase instead of clobbering your prompt.
- apt-only modules stop early under
--only/--skip: a filtered run executes the manifest modules directly rather than the pacman-aware default path. The modules that can only install with apt (base.system,shell.zsh,cli.modern,lang.go,network.tailscale,tools.vault,db.postgres18) stop before changing anything and print thepacmancommand to run instead.db.postgres18instead points to an unfiltered run: its check only recognizes the Ubuntu packages, and the default Arch path installs and initializes PostgreSQL with pacman.
Everything else — language runtimes, AI agents, and the flywheel tool stack — installs identically to Ubuntu.
Cloud agent environments
The public cloud agent setup guide selects the right recipe for your provider. Claude Code and ChatGPT / Codex have hosted test evidence; Amp Orbs, Devin and Grok Bot have documentation-based recipes awaiting hosted acceptance. Meta Muse needs shell/runtime and persistence checks before an installation recipe can be accepted. The installer retains its original filename and supports claude, codex and provider-neutral generic modes.
Prefer to hand the setup to a browser agent? Use Copy full instructions near the top of the guide, or share its complete ChatGPT / Codex Markdown brief. The Markdown is the exact copy-button content, including scripts, screenshot references and verification steps. Choose agent=claude, codex, amp, devin, grok, muse or generic; omitting agent defaults to Claude. The guide's Open Markdown link follows your selected provider.
Claude Code on the web
Claude Code on the web runs cloud sessions on a disposable Ubuntu 24.04 VM. ACFS uses scripts/claude-code-web-setup.sh to install prebuilt agent-facing tools during setup, without provisioning users, upgrading the OS or starting background services. The cloud guide walks through it with copy buttons.
Set it up: in claude.ai/code open the environment menu, choose Add cloud environment (or edit one), set Network access to Full (recommended), and paste this as the Setup script:
#!/bin/bash
(
acfs_cloud_setup="$(curl -q -fsSL --proto '=https' --proto-redir '=https' --connect-timeout 5 --max-time 20 -A 'acfs (+https://github.com/Dicklesworthstone/agentic_coding_flywheel_setup)' -H 'Accept-Encoding: identity' https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/scripts/claude-code-web-setup.sh)" || { printf '%s\n' 'ACFS cloud bootstrap download failed; tools were not installed. Check network access and retry.' >&2; exit 0; }
printf '%s\n' "$acfs_cloud_setup" | bash
)The bootstrap downloads completely before it runs, with a 20-second transfer limit. If that download fails, Claude reports the failure and still starts the session. Full access enables the public binary mirror. Custom and restricted-mode behavior are described below. Pasting the whole script into the field works too.
| Tool | Command | In a cloud session |
|---|---|---|
| BeadsRust | br | Issue tracking in the repo's .beads/ |
| Beads Viewer | bv | bv --robot-* triage from a prebuilt release |
| MCP Agent Mail | am, mcp-agent-mail | Registers am serve-stdio with Claude Code, so no daemon has to survive the snapshot |
| Ultimate Bug Scanner | ubs | Scanner with bundled modules/helpers; no hooks installed |
| Coding Agent Session Search | cass | Search this VM's agent history |
| CASS Memory System | cm | Procedural memory |
| Meta Skill | ms | Skill search and management |
| ast-grep | ast-grep | Structural search and UBS dependency |
| Jeffrey's Skills | jsm | Skill manager for jeffreys-skills.md |
| JeffreysPrompts | jfp | Prompt library CLI |
How it behaves:
- Every prebuilt bundle is checked against the repository's
cloud-mirror.jsonbefore extraction. A mismatch skips the tool. All tools, including JSM, are hash-pinned. - Tool jobs run in parallel with a maximum 180-second deadline covering downloads and both existing/final binary checks, plus up to two seconds for termination. This keeps setup inside the roughly five-minute caching window under normal VM operation. No compiler is invoked. Later sessions start from the snapshot with the tools already installed.
- The script always exits 0, because a failing setup script stops the session from starting. Anything that did not install is listed in the summary, in
~/.acfs/cloud/setup.log, and in the guide below. - It writes a managed block into
~/.claude/CLAUDE.md(or$CLAUDE_CONFIG_DIR/CLAUDE.md), which cloud sessions load as user instructions, so Claude knows which flywheel tools exist and how to call them. Content outside the block is preserved. If existing managed markers are incomplete, setup leaves the file untouched and logs the retained replacement guide's location. - With a custom
ACFS_CLOUD_ROOT, new Claude Agent Mail MCP registrations keep their archive in that writable root, preserving an explicitSTORAGE_ROOTorXDG_DATA_HOME. The generated CLI recipes and rerun command retain the same setup-selected archive when a later task no longer has those environment overrides; relative setup paths are resolved from the setup directory. Explicit task-timeSTORAGE_ROOTvalues still win. Setup does not copyDATABASE_URLcredentials into the registration or guide. Re-runs repair the exact old ACFS entry that launchedmcp-agent-mailwithout arguments, retaining a private config backup. Existing registrations retain their storage configuration; use the guide's CLI recipe if a retained registration points at an unwritable location.
Left out on purpose: everything in install.sh that provisions a long-lived machine (users, zsh theming, the Ubuntu upgrade, systemd services, Tailscale, PostgreSQL, Vault, cloud CLIs), plus ntm (no interactive terminal to drive), dcg (it installs a user-level Claude Code hook, and cloud sessions only run hooks from the repository's .claude/settings.json), rch (needs SSH build workers), and caam, ru, slb.
Options, set inline so the setup script itself sees them (for example curl -fsSL … | ACFS_CLOUD_TOOLS="br bv am ubs ast-grep" bash). Selecting UBS automatically includes ast-grep so its structural checks are available without a first-scan download:
| Variable | Default | Effect |
|---|---|---|
ACFS_CLOUD_TOOLS | br bv am ubs cass cm ms ast-grep jsm jfp | Which tools to install; ubs also selects ast-grep |
ACFS_CLOUD_TIMEOUT | 180 | Deadline for each tool's job (download, install and version checks) in seconds; 1–180 |
ACFS_CLOUD_REINSTALL | 0 | 1 reinstalls tools that are already on PATH (to update inside a running session) |
ACFS_CLOUD_AGENT | claude | codex writes a Codex instruction guide and skips Claude MCP registration |
ACFS_CLOUD_ROOT | $HOME | Writable absolute data root for binaries/logs; Codex's custom-root guide lives there too |
ACFS_CLOUD_SKILL_DIR | Unset | Optional absolute Codex repository skill directory; existing skills are retained |
ACFS_REF | main | ACFS ref that supplies cloud-mirror.json |
When choosing a custom ACFS_CLOUD_ROOT in generic mode, replace the $HOME paths in the task instructions with that root. Its guide and log are <root>/.acfs/cloud/AGENTS.md and <root>/.acfs/cloud/setup.log. The generated guide supplies the exact PATH and data commands, including Agent Mail's command-local storage setting.
Once the setup script is available, reruns reuse working tools without downloading the manifest or bundles. If a tool is missing, fails its version check, or ACFS_CLOUD_REINSTALL=1 is set, parallel install jobs share one manifest download within their existing deadlines. Download failures remain visible and never trigger source builds.
Public prebuilt mirror: every default tool, including ast-grep, JSM and JFP, comes from https://downloads.agent-flywheel.com/acfs-cloud/v1. No login, GitHub token or repository ownership is needed. Select Full network access, or Custom allowing raw.githubusercontent.com and downloads.agent-flywheel.com. Bundles are pinned by SHA256 in cloud-mirror.json, use content-addressed URLs, and are checked before extraction and executable verification. Setup never runs upstream installers or builds from source.
Trusted/degraded mode: when the mirror cannot be reached, setup tries the exact public artifact pinned in the same manifest. JSM uses a vendor-verified copy on public GitHub, with its original checksum and upstream URL retained. UBS uses a complete public copy of the mirror bundle, including its release-pinned modules and helpers, so scans need no module downloads after setup. The environment's GitHub proxy may block these downloads too, so Trusted does not guarantee all tools. Existing working tools remain available, failures explain the Full/Custom setting in the summary, and session startup continues.
The bundles target Linux x86_64. Each executable must successfully return its version before installation. JFP uses the baseline x64 release asset, verified on both Ivy Bridge without AVX2 and EPYC with AVX2. The publisher requires that asset and refuses to substitute a modern-only build.
Maintainer refresh: python3 scripts/cloud-mirror-publish.py --stage /path/on/large-disk/acfs-cloud --output /path/to/new-candidate.json --publish discovers latest releases with authenticated gh, verifies release checksums and pinned Minisign signatures where published, bundles Linux x86_64 executables, uploads with authenticated Wrangler, and verifies each public download. It requires Python 3.10+, gh, minisign, and wrangler; these credentials are needed only by the maintainer. Review the candidate, smoke-test on Ubuntu 24.04, and commit it as cloud-mirror.json. Repeated runs reuse downloaded artifacts and skip matching remote bundles. Staging is retained for inspection. A failed tool aborts manifest publication; it cannot silently disappear from the install set.
Use --tools br bv to refresh a subset while retaining every other entry from the committed manifest. Partial refreshes require its existing base URL. Refreshing JSM also publishes its verified vendor archive to the public cloud-binaries-v1 prerelease. Assets include the source checksum in their names, are never replaced, and must pass both GitHub's digest check and anonymous download verification before the candidate manifest is written. This carrier does not change ACFS's normal latest release. When mirroring to another R2 bucket/domain, --base-url supplies the public prefix and --bucket supplies the destination bucket; the URL path becomes the object prefix.
ChatGPT / Codex cloud environments
The same public bundles work in a Linux x86_64 Codex cloud environment. In Work in → Cloud, create or edit an environment and put this in its Install script:
#!/bin/bash
(
set -o pipefail
acfs_cloud_root="$(git rev-parse --show-toplevel)" || exit 1
acfs_cloud_setup="$(curl -q -fsSL --proto '=https' --proto-redir '=https' --connect-timeout 5 --max-time 20 -A 'acfs (+https://github.com/Dicklesworthstone/agentic_coding_flywheel_setup)' -H 'Accept-Encoding: identity' https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/scripts/claude-code-web-setup.sh)" || { printf '%s\n' 'ACFS cloud bootstrap download failed; tools were not installed. Check network access and retry.' >&2; exit 1; }
printf '%s\n' "$acfs_cloud_setup" | ACFS_CLOUD_SKILL_DIR="$acfs_cloud_root/.agents/skills/acfs-cloud-tools" ACFS_CLOUD_AGENT=codex ACFS_CLOUD_ROOT="$acfs_cloud_root/.acfs-cloud" bash || exit 1
acfs_cloud_exclude="$(git rev-parse --git-path info/exclude)" || exit 1
mkdir -p "$(dirname "$acfs_cloud_exclude")" || exit 1
printf '\n/.acfs-cloud/\n/.agents/skills/acfs-cloud-tools/\n' >> "$acfs_cloud_exclude"
) || exit 1Enable internet access and add raw.githubusercontent.com and downloads.agent-flywheel.com to Additional allowed domains. These public downloads need no secrets or ownership of the tool repositories. The recipe installs into the writable repository workspace because hosted home/config paths can be read-only, and excludes its data directory through local Git metadata. It preserves HOME and CODEX_HOME. Review <repo>/.acfs-cloud/.acfs/cloud/setup.log, then Publish the prepared environment. After changing its setup, Republish for new tasks. See OpenAI's current cloud environment guide.
Save these instructions in the environment's Start skill and include them at the start of each new task. Our hosted checks reused all eleven executables but did not automatically load the saved Start skill or generated repository skill; explicitly loading the guide and setting PATH works:
Find the repository root with git rev-parse --show-toplevel.
Read <repo>/.acfs-cloud/.codex/AGENTS.md for the installed flywheel tools and <repo>/.acfs-cloud/.acfs/cloud/setup.log for failures.
In each task shell, run acfs_cloud_root="$(git rev-parse --show-toplevel)/.acfs-cloud"; export PATH="$acfs_cloud_root/.local/bin:$PATH" before using the tools.
Follow the guide's CASS_DATA_DIR, CASS_MEMORY_HOME and JFP_HOME exports in each task shell so search data, memory and prompt caches use the writable workspace; preserve existing overrides and keep any XDG_DATA_HOME/XDG_CONFIG_HOME writable.
Before using Agent Mail with a read-only HOME, follow the guide's command-scoped STORAGE_ROOT recipe; reuse existing configuration and preserve DATABASE_URL.
Before using JSM with a read-only HOME, follow the guide's command-scoped XDG recipe; reuse existing configuration and check skill installation targets separately.
Keep JSM_DISABLE_SELF_UPDATE=1 in each task shell so ordinary JSM commands retain the manifest-pinned executable; use verified cloud setup for tool refreshes.
Before using MS, follow the guide's local initialization and indexing steps in a writable project; reuse existing .ms/MS_ROOT/MS_CONFIG state.
Check br --version, bv --version, ubs --version and jsm --version before starting work.
Use br ready --json and bv --robot-triage; never open their interactive TUIs.Codex mode preserves existing instructions outside its managed block. Without a custom root it uses $CODEX_HOME/AGENTS.md or ~/.codex/AGENTS.md; a nonempty AGENTS.override.md takes precedence, so setup warns. The workspace recipe also creates .agents/skills/acfs-cloud-tools/SKILL.md in Codex's documented repository skill location; an existing skill at that path is preserved. Reading that skill loads the separate guide and configures PATH. Automatic hosted catalog discovery failed in our checks: a fresh task still omitted it after removing its Git exclusion, so changing exclusions is not a proven fix. Use the explicit startup instructions above. See OpenAI's instruction discovery rules. Agent Mail is available as a CLI; this setup does not configure or claim hosted MCP support. Local Codex MCP configuration and ChatGPT's remote MCP connections are different surfaces; see OpenAI's MCP guide.
If your UI instead has Setup script and Maintenance script, it uses the legacy environment workflow. Put the install command in Setup script; avoid reinstalling in Maintenance script on every cached resume. Configure task-phase internet separately if tools need online access after setup.
Verification boundary: both installer modes are tested on Ubuntu 24.04, including real public downloads. On 2026-10-08, a fresh Claude-hosted Full session pinned to 4a0e6bfa792ce73ad4c69847755ecd64384288c2 installed all ten tools in 12 seconds; all eleven versions passed, the guide loaded automatically, and Agent Mail's stdio MCP health check passed. On 2026-10-09, a cold default-Trusted Claude session pinned to 31c8180860f9ed461d9843d6ed298939a5b3ea74 installed all ten tools through public fallbacks in 53 seconds; all eleven versions, automatic instructions and actual Agent Mail MCP health passed. That run's first UBS helper download failed TLS; its release-hash-verified recovery was tested separately. The later complete UBS fallback removes that lazy module download, but has not been rerun in that hosted session. Codex hosted installation took 6 seconds at 140a845d; fresh published tasks reused all eleven executables, including after the repository-skill update at 4bdbf5a7. A task that explicitly loaded the guide configured PATH and passed all eleven probes. Automatic Codex skill loading remains open. These times describe the pinned earlier recipes, not a fresh hosted acceptance of later changes. The script reports individual failures but exits zero; successful session startup alone does not prove every tool installed. Default UBS exit 0 can include warnings; inspect its report or use --ci --fail-on-warning for a warning-strict check.
Other Linux cloud agents
The provider-neutral template writes $HOME/.acfs/cloud/AGENTS.md and does not change agent configuration or register MCP servers:
#!/bin/bash
(
set -o pipefail
acfs_cloud_setup="$(curl -q -fsSL --proto '=https' --proto-redir '=https' --connect-timeout 5 --max-time 20 -A 'acfs (+https://github.com/Dicklesworthstone/agentic_coding_flywheel_setup)' -H 'Accept-Encoding: identity' https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/scripts/claude-code-web-setup.sh)" || { printf '%s\n' 'ACFS cloud bootstrap download failed; tools were not installed. Check network access and retry.' >&2; exit 1; }
printf '%s\n' "$acfs_cloud_setup" | ACFS_CLOUD_AGENT=generic bash
) || exit 1Include these instructions in each task:
Read $HOME/.acfs/cloud/AGENTS.md and $HOME/.acfs/cloud/setup.log before starting work.
In each task shell, run export PATH="$HOME/.local/bin:$PATH".
If using a custom writable data root, follow the guide's CASS_DATA_DIR, CASS_MEMORY_HOME and JFP_HOME exports in each task shell; preserve existing overrides and keep any XDG_DATA_HOME/XDG_CONFIG_HOME writable.
Before using Agent Mail with a read-only HOME, follow the guide's command-scoped STORAGE_ROOT recipe; reuse existing configuration and preserve DATABASE_URL.
Before using JSM with a read-only HOME, follow the guide's command-scoped XDG recipe; reuse existing configuration and check skill installation targets separately.
Keep JSM_DISABLE_SELF_UPDATE=1 in each task shell so ordinary JSM commands retain the manifest-pinned executable; use verified cloud setup for tool refreshes.
Before using MS, follow the guide's local initialization and indexing steps in a writable project; reuse existing .ms/MS_ROOT/MS_CONFIG state.
Check br --version, bv --version, ubs --version and jsm --version; report missing tools from the setup log.
Use the existing repository tracker with br ready --json and bv --robot-triage. Never open their interactive TUIs.
Agent Mail is available as a CLI. This setup does not configure this agent's MCP servers.Requirements: Linux x86_64 with compatible runtime libraries, Bash, Python 3, curl, tar and GNU timeout, plus a writable data root. Ubuntu 24.04 has full install checks. On 25 October 2026, all eleven current executables also passed offline version checks in a clean Debian 12 x86_64 container with glibc 2.36; this is executable startup evidence, not a Debian installation or hosted-provider test. If you set ACFS_CLOUD_ROOT, use that root instead of $HOME in these instructions. Inspect the setup log and each version command to confirm the installed tools work.
For a custom data root, follow the generated guide's CASS_DATA_DIR and CASS_MEMORY_HOME exports so search data and memory also use the writable workspace, and its JFP_HOME export for prompt caching. These preserve existing overrides and an explicit XDG_DATA_HOME; configured state directories must be writable. JFP also honors XDG_CONFIG_HOME, which takes precedence and must be writable. Provider HOME and configuration are preserved.
Keep JSM pinned: the generated task-shell command exports JSM_DISABLE_SELF_UPDATE=1, and its JSM command examples apply the same opt-out. Otherwise even an ordinary command such as jsm config show can automatically replace the verified binary with a newer vendor release. Saved JSM preferences stay unchanged. For an intentional refresh or to restore the reviewed binary after an automatic update, run the verified cloud setup with ACFS_CLOUD_REINSTALL=1; ordinary reruns reuse working binaries. Existing repository skills are preserved, so use the updated generated guide if a saved skill predates this setting. Avoid jsm update in the pinned environment.
Agent Mail without MCP: the generated guide includes a CLI session recipe using am macros start-session, captures the returned agent name, and reads messages with am robot inbox --include-bodies --json. It also shows how to reserve a specific edit path and release only that reservation. Custom-root recipes apply the same command-scoped storage defaults while preserving DATABASE_URL. These operations need no background service; agents must share the same mailbox to coordinate across tasks. Separate provider VMs do not share local mail automatically.
MS needs project state before it can search skills. In a writable project, run ms init --robot once, then ms index <skill-directory> --robot and ms search "query" --robot. Replace <skill-directory> with the directory containing your skills, such as .agents/skills. Reuse existing .ms, MS_ROOT or MS_CONFIG state; with configured overrides, skip this initialization recipe. Avoid --global on a read-only HOME and --force when preserving existing state. Setup only installs MS; it leaves project initialization and skill indexing to the task that needs them.
- Amp Orbs: merge the command into executable
.agents/setup, or use the project's Pre-setup Script. Do not install dependencies in.agents/resume. Amp documents Debian 12; the current binaries pass the clean Debian 12 version checks above, while Amp-hosted installation, instruction loading and snapshot persistence remain unverified. Setup does not need personal GitHub credentials. - Devin: add a Linux blueprint
runstep toinitializeormaintenanceand task instructions toknowledge. Verify a fresh snapshot. PATH exports do not persist between blueprint steps unless written to$ENVRC; explicit task-shell exports avoid assuming persistence. - Grok Bot: Enterprise Team Setup runs shell-script manifest entries as the computer user on every Linux team computer. Consumer setup access, binary compatibility and hosted persistence remain unverified. Grok Build CLI and chat Build Mode are different products.
- Meta Muse: Meta documents a persistent Linux VM with Sentinel-controlled access. A usable startup hook, CPU/runtime compatibility and hosted installation have not been verified. Muse Code is the separate terminal/CI agent.
The Installer
The installer is the heart of ACFS—a modular Bash script that transforms a fresh Ubuntu or Arch-based machine into a fully-configured development environment.
Usage
Full vibe mode (recommended for throwaway VPS):
{ acfs_installer="$(curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh")" || acfs_installer="$(curl -fsSL "https://cdn.jsdelivr.net/gh/Dicklesworthstone/agentic_coding_flywheel_setup@main/install.sh")"; } && printf '%s\n' "$acfs_installer" | bash -s -- --yes --mode vibeInteractive mode (asks for confirmation):
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh" | bashSafe mode (no passwordless sudo, agent confirmations enabled):
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh" | bash -s -- --mode safeInstaller Modes
| Mode | Passwordless Sudo | Agent Flags | Best For |
|---|---|---|---|
| vibe | Yes | --dangerously-skip-permissions | Throwaway VPS, maximum velocity |
| safe | No | Standard confirmations | Production-like environments |
Installation Phases
graph TD
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#e8f5e9', 'lineColor': '#90a4ae'}}}%%
A["Phase 1: User Setup<br/><small>Create ubuntu user, migrate SSH keys, sudo</small>"]
B["Phase 2: Filesystem<br/><small>/data/projects, ~/.acfs layout</small>"]
C["Phase 3: Shell Setup<br/><small>zsh, oh-my-zsh, powerlevel10k</small>"]
D["Phase 4: CLI Tools<br/><small>ripgrep, fzf, lazygit, Tailscale, etc.</small>"]
E["Phase 5: Language Runtimes<br/><small>bun, uv, rust, go, nvm</small>"]
F["Phase 6: AI Agents<br/><small>claude, codex, agy</small>"]
G["Phase 7: Cloud & Database<br/><small>PostgreSQL, vault, wrangler, supabase, vercel</small>"]
H["Phase 8: Flywheel Stack<br/><small>ntm, agent mail, br, bv, ubs, dcg, ru, etc.</small>"]
I["Phase 9: Finalize<br/><small>Deploy acfs.zshrc, tmux.conf, agent guide</small>"]
J["Post-install smoke test<br/><small>critical checks; then run acfs doctor</small>"]
A --> B --> C --> D --> E --> F --> G --> H --> I --> J
classDef phase fill:#e8f5e9,stroke:#81c784,stroke-width:2px,color:#2e7d32
class A,B,C,D,E,F,G,H,I,J phaseKey Properties
| Property | Description |
|---|---|
| Idempotent | Safe to re-run; skips already-installed tools |
| Checkpointed | Phases resume automatically from ~/.acfs/state.json |
| Pre-flight validated | Run scripts/preflight.sh to catch issues before install |
| Logged | Colored output with progress indicators |
| Modular | Each category is a separate sourceable script |
Claude Code Settings Written at Install
The installer provisions a few keys in ~/.claude/settings.json, always merging non-destructively (a value you have already set is never overridden):
| Key | Value | Mode | Why |
|---|---|---|---|
cleanupPeriodDays | 99999 | all modes | Claude Code silently deletes session transcripts older than this (default 30 days) from ~/.claude/projects — with no warning or log line. That default destroys history before cass can index it, so ACFS sets an explicit high value; lower it yourself if you actually want pruning. |
skipDangerousModePermissionPrompt | true | vibe only | Avoids interactive workspace-trust prompts for coding agents. |
Resume Capability
The installer tracks progress in ~/.acfs/state.json. Re-run the same command
with --resume after fixing a failed dependency, privilege problem, or interrupted
installation. Completed phases are checked against the selected modules' live
verification results before they can be skipped. Incomplete phases retry their
component checks, so working components are kept and missing or broken ones are
repaired. This also recovers older installations whose completion records were
written despite a failed step; do not delete the state file to recover them.
Required step failures remain recorded until a successful retry or verified
recovery resolves them. Optional failures remain visible without blocking an
otherwise successful installation; an optional module explicitly requested with
--only must succeed. Final success requires the selected essential components
to pass verification, including for targeted installations.
To force a fresh reinstall of all tools:
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh" | bash -s -- --yes --mode vibe --force-reinstallPre-Flight Check
Before running the full installer, validate your system:
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/scripts/preflight.sh" | bash
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/scripts/preflight.sh" | bash -s -- --json
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/scripts/preflight.sh" | bash -s -- --format toon
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/scripts/preflight.sh" | bash -s -- --network=skipThis checks:
- OS compatibility (Ubuntu 22.04+ or Arch-family: Arch, Omarchy; supported LTS releases stay in place unless an upgrade is explicitly requested)
- Architecture (x86_64 or ARM64)
- Memory and disk space (warns below 4GB RAM; fails below 20GB free disk)
- Network connectivity to required URLs
- Cached
checksums.yamlavailability for verified upstream installers - APT lock status
- Potential conflicts (nvm, pyenv, existing ACFS)
Network checks performed:
| Check | What it verifies | Fix if failing |
|---|---|---|
| DNS resolution | Can resolve github.com, raw.githubusercontent.com | Check provider DNS settings; inspect resolvectl status or /etc/resolv.conf |
| GitHub HTTPS | Can reach github.com:443 | Check firewall, proxy, or VPN settings |
| Verified installer URLs | Critical upstream installer endpoints from checksums.yaml plus ACFS raw content | May need to retry; transient failures OK; checksum verification still stays enabled |
| APT mirrors | Default Ubuntu mirror reachable | Check /etc/apt/sources.list or try different mirror |
| Offline/cache mode | --network=skip skips live URL checks while still reporting local checksum availability | Re-run with --network=check when online before a release or difficult install |
For checksum-refresh review, compare a generated candidate without changing checksums.yaml:
candidate="/tmp/acfs-checksums.$$.candidate.yaml"
./scripts/lib/security.sh --update-checksums > "$candidate"
./scripts/preflight.sh --checksum-candidate "$candidate"Common preflight failures:
| Error | Cause | Solution |
|---|---|---|
| "Cannot resolve github.com" | DNS misconfigured | Check provider DNS settings or reboot; do not overwrite managed resolver files |
| "Cannot reach github.com" | Firewall blocking HTTPS | Allow outbound port 443 |
| "timeout contacting github.com" | Network, proxy, or provider route is slow | Retry with --network=check; if it persists after install bootstrap, run acfs support-bundle |
| "APT mirror slow or unreachable" | Regional mirror down | Edit /etc/apt/sources.list to use archive.ubuntu.com |
| "checksum candidate differs" | Upstream verified installer content changed | Review the diff; do not install from unverified fallback sources |
| "APT is locked by another process" | Another apt process running (usually unattended-upgrades on a fresh VPS) | Wait for it to finish; reboot and resume if it remains stuck |
| "Need at least 20GB free" | Less than 20GB free disk (a hard failure; low RAM only warns) | Clean up with sudo apt autoremove or expand disk |
Console Output
The installer uses semantic colors for progress visibility:
[1/8] Installing essential packages... # Blue: progress steps
Installing zsh, git, curl... # Gray: details
⚠️ May take a few minutes # Yellow: warnings
✖ Failed to install package # Red: errors
✔ Shell setup complete # Green: successOptional Ubuntu Release Upgrade
A no-flags install keeps supported Ubuntu 22.04 and 24.04 LTS hosts on their current release. Non-root users with sudo and Ubuntu 24.04 Docker/WSL environments can install ACFS without opting into an OS upgrade.
Pass --target-ubuntu=26.04 to explicitly request an upgrade to Ubuntu 26.04 LTS. An OS upgrade requires a root-run installer on a host that can reboot; it is separate from installing or updating the ACFS tools.
How it works:
- Detects your current Ubuntu version
- Calculates a supported upgrade path (e.g., 24.04 → 26.04 LTS)
- Performs sequential
do-release-upgradeoperations - Reboots after each upgrade (handled automatically)
- Resumes via systemd service after reboot
- Continues ACFS installation once at target version
Expected timeline:
- Each version hop takes 30-60 minutes
- Multiple LTS hops take longer; the path must be offered by Ubuntu's stable release upgrader
- SSH sessions disconnect during reboots (reconnect to monitor)
To explicitly request the 26.04 LTS upgrade:
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh" | bash -s -- --yes --mode vibe --target-ubuntu=26.04To suppress an explicit upgrade request:
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh" | bash -s -- --yes --mode vibe --target-ubuntu=26.04 --skip-ubuntu-upgrade--skip-ubuntu-upgrade wins regardless of flag order. Neither this flag nor an ordinary no-target install bypasses an active system upgrade checkpoint: normal installation remains blocked until the unfinished upgrade is resolved. Inspect the checkpoint and resume logs before retrying; keep the recovery state intact.
Monitoring upgrade progress:
# Check current status
/var/lib/acfs/check_status.sh
# View upgrade logs
journalctl -u acfs-upgrade-resume -f
# View detailed logs
tail -f /var/log/acfs/upgrade_resume.logImportant notes:
- Create a VM snapshot before upgrading (recommended but not required)
- Upgrades cannot be undone without restoring from snapshot
- The system will reboot multiple times automatically
- Ubuntu 25.10 is supported only as a recovery source when explicitly upgrading to 26.04 LTS; it is not an upgrade destination
- Reconnect via SSH after each reboot to monitor progress
The Update Command
After installation, keeping tools current is handled by acfs-update. It provides a unified interface for updating all installed components.
Usage
acfs-update # Update apt, runtimes, shell, agents, cloud CLIs, and stack tools
acfs-update --agents-only # Only update coding agents
acfs-update --runtime-only # Only update runtimes (bun, rust, uv, go)
acfs-update --dry-run # Preview changes without making them
acfs-update --yes --quiet --no-self-update
# Automated mode that avoids changing the ACFS tree itself
acfs-update --bootstrap-self-update
# Explicitly convert a non-git or incomplete install into a git checkout--bootstrap-self-update replaces ACFS repository files with origin/main. Copy local files elsewhere or commit local edits before opting in. Routine updates leave non-git and incomplete Git installs untouched.
The control-plane boundary: with --no-self-update (the shipped nightly
timer's default), automated runs update your stack tools but never ACFS itself --
neither the git checkout nor the deployed runtime copies under ~/.acfs
(bin/acfs, bin/acfs-update, scripts/lib/*.sh). The control plane therefore
needs its own periodic refresh: run acfs-update without --no-self-update from
time to time, or git pull --rebase the checkout and then run
acfs-update --shell-only to redeploy the runtime copies. acfs doctor warns
(updates.runtime_skew) when the checkout and the deployed runtime disagree, so
a stack kept current by the nightly cannot silently outrun a stale dispatcher.
Temp space: if TMPDIR (default /tmp) refuses a test write, because it is
full or over a per-user tmpfs quota, acfs-update moves the run to the first of
ACFS_UPDATE_TMPDIR, /data/tmp, /var/tmp and ~/.cache/acfs/tmp that
accepts one. Verified installers are staged in a system directory instead of
TMPDIR: /tmp, or /var/tmp when /tmp refuses the write.
What Gets Updated
| Category | Tools | Method |
|---|---|---|
| System | apt packages | apt update && apt upgrade |
| Shell | OMZ, P10K, plugins | git pull on each repo |
| Shell | Atuin, Zoxide | Re-run upstream installers |
| Runtime | Bun | bun upgrade |
| Runtime | Rust | rustup update stable |
| Runtime | uv (Python) | uv self update |
| Runtime | Go | apt upgrade (if apt-managed) |
| Agents | Claude Code | claude update --channel latest |
| Agents | Codex | bun install -g @latest |
| Agents | Antigravity | agy update (or verified installer with --force) |
| Agents | oh-my-pi (omp) | omp update (or verified installer with --force) |
| Agents | Grok CLI | Re-run verified installer (GROK_BIN_DIR pinned to ACFS bin dir) |
| Cloud | Wrangler, Vercel | bun install -g @latest |
| Cloud | Supabase | GitHub release tarball (sha256 checksums) |
| Stack | ntm, slb, ubs, dcg, ru, etc. | Re-run upstream installers |
Options
Category Selection:
--apt-only Only update system packages
--agents-only Only update coding agents
--cloud-only Only update cloud CLIs
--shell-only Only update shell tools (OMZ, P10K, plugins, Atuin, Zoxide)
--runtime-only Only update runtimes (bun, rust, uv, go)
--stack Include Agent Flywheel stack tools (enabled by default)Skip Categories:
--no-apt Skip apt updates
--no-agents Skip agent updates
--no-cloud Skip cloud CLI updates
--no-shell Skip shell tool updates
--no-runtime Skip runtime updates (bun, rust, uv, go)Behavior:
--force Install missing tools (not just update existing)
--dry-run Preview changes without making them
--yes, -y Non-interactive mode (skip prompts)
--quiet, -q Minimal output (only errors and summary)
--verbose, -v Show detailed command output
--abort-on-failure Stop on first failure (default: continue)Per-Tool Version Holds
When a single tool ships a regression, hold just that tool instead of pausing the whole nightly. Every hold records an owner, a reason, and an optional expiry, so a hold is a visible decision rather than silent version drift:
acfs hold br --version 0.4.1 --reason "0.5.2 cannot read existing beads DBs" --expiry 2026-09-15
acfs holds # list holds (also surfaced by `acfs doctor` and every update summary)
acfs unhold br # release the holdHeld tools are skipped by every update run with the owner/reason/expiry named
in the log. Expired holds warn and are ignored. Holds live in
~/.acfs/holds.yaml.
Post-Install Verification and Rollback
Before a verified installer replaces a tool binary, the previous binary is
retained at <binary>.prev. After the install, a smoke check probes the fresh
binary with a per-tool argument (update_tool_smoke_probe in
scripts/lib/update.sh: fsfs version, mdwb --help, pfr --help) or, for
tools without an entry, the chain --version → --help → version. Every
probe is headless-safe and runs with stdin detached under a 20 s ceiling.
Verdicts:
- healthy — a probe exited 0.
- unsupported — every probe was rejected as an unknown argument (usage
error). The binary ran, so it is kept and logged as
POST-INSTALL VERIFICATION UNAVAILABLE; no rollback, no backoff. - broken — a probe timed out, could not execute, died by signal, or failed
without a usage-style rejection (crash, traceback, missing runtime). The
previous binary is restored atomically and the failure is recorded in
~/.local/state/acfs/update-rollback.stateso nightly runs back off instead of reinstalling the same broken release every night.
acfs doctor reports rolled back tools; acfs-update --force retries
immediately. A backoff also ends as soon as the tool's pinned installer
changes or its GitHub repo publishes a new release, because neither has
failed yet.
acfs-update exit codes distinguish partial from total failure: 0 all
succeeded, 1 total failure (nothing updated), 2 partial failure (some tools
updated, some failed).
Logs
Update logs are automatically saved to ~/.acfs/logs/updates/ with timestamps:
# View most recent log
cat ~/.acfs/logs/updates/$(ls -1t ~/.acfs/logs/updates | head -1)
# Follow a running update
tail -f ~/.acfs/logs/updates/$(ls -1t ~/.acfs/logs/updates | head -1)Why Separate from the Installer?
The installer transforms a fresh VPS. The update command maintains an existing installation. Separating them allows:
- Focused updates: Update just agents without touching system packages
- Dry-run previews: See what would change before committing
- Skip flags: Temporarily exclude categories that are working fine
- Stack control: Stack updates are included by default; skip with
--no-stack - Automated updates: Run via cron with
--yes --quiet
ACFS CLI Commands
After installation, the acfs command provides a unified interface for managing your environment. Each subcommand is designed to be fast, informative, and scriptable.
Quick Reference
acfs info # Lightning-fast system overview
acfs cheatsheet # Discover installed aliases
acfs dashboard generate # Generate HTML status page
acfs doctor # Health checks
acfs newproj # Create a new project (TUI or CLI)
acfs agents update # Regenerate the flywheel agent guide (ACFS-owned)
acfs agents install --help # Explicitly deploy the guide (never overwrites)
acfs update # Update all tools
acfs holds # List per-tool version holds (acfs hold/unhold to manage)
acfs services status # Check Agent Mail, CM, and CASS daemons
acfs services-setup # Configure agent credentials
acfs continue # View upgrade progress after reboot
acfs installer-cache build # Cache checksum-pinned installer entrypointsacfs installer-cache build — Verified Installer Entrypoint Cache
Build a portable acfs-installer-cache/ directory containing the
checksum-pinned upstream installer scripts selected from acfs.manifest.yaml.
On Ubuntu, pass either that directory or its parent to a later installation:
acfs installer-cache build --output /mnt/acfs-cache
./install.sh --yes --verified-installer-cache /mnt/acfs-cacheExplicit cache selection is fail-closed: if a required cached entrypoint is
missing, stale, malformed, for another Ubuntu/architecture target, or does not
match the current manifest and checksums.yaml, ACFS refuses it instead of
fetching that entrypoint live.
This is not a complete offline installer. The cached entrypoint scripts still
need network access for release archives, package registries, Git repositories,
APT packages, and other transitive payloads. The initial ACFS bootstrap is also
separate; use --bootstrap-archive for that layer. The roadmap below retains
full pre-downloaded package bundles as future work.
acfs newproj — New Project Wizard
Create a new project directory with ACFS defaults (git init, optional br/beads, Claude settings, AGENTS.md). The interactive wizard is recommended for beginners. Its file creation requires Python 3. It rejects symlink destinations and parent directories, and creates files exclusively so existing or concurrently created files are preserved. New directories and files are capped at 0755 and 0644; a stricter caller umask still applies. A failed write leaves the partial file in place for inspection; it does not roll back the project. While the wizard remains open, Retry is bound to the original directory, not just its name. If that directory is moved or replaced, restore it to retry, or choose another directory; the replacement cannot inherit completed steps from the old project.
Interactive wizard (recommended):
acfs newproj --interactive
acfs newproj -i
acfs newproj -i myapp # Prefill project nameThe wizard guides you through:
- Project naming and location
- Tech stack detection/selection
- Feature selection (br/beads, Claude settings, AGENTS.md, UBS ignore)
- AGENTS.md customization preview
Wizard diagnostics use private session files (mode 0600) and create new log
directories with mode 0700, without changing your shell's umask or existing
directory permissions. Repeated sessions get distinct files. Edited AGENTS.md
contents remain in the project; state logs record only their length.
TUI Wizard Screenshots
Welcome Screen:
╔═══════════════════════════════════════════════════════╗
║ ║
║ █████╗ ██████╗ ███████╗ ███████╗ ║
║ ██╔══██╗██╔════╝ ██╔════╝ ██╔════╝ ║
║ ███████║██║ █████╗ ███████╗ ║
║ ██╔══██║██║ ██╔══╝ ╚════██║ ║
║ ██║ ██║╚██████╗ ██║ ███████║ ║
║ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ ║
║ ║
║ Agentic Coding Flywheel Setup ║
║ ║
╚═══════════════════════════════════════════════════════╝
This wizard will help you set up a new project with:
✓ Project directory structure
✓ Git repository initialization
✓ AGENTS.md for AI coding assistants
✓ Beads issue tracking (optional)
✓ Claude Code settings (optional)Confirmation Screen:
──────────────────── Review & Confirm ────────────────────
Step 7 of 9
Please review your selections before creating the project.
Project Summary
──────────────────────────────────────────────────────────
Name: myapp
Location: /home/user/projects/myapp
Tech: Node.js, TypeScript
Features
──────────────────────────────────────────────────────────
✓ Beads tracking
✓ Claude Code settings
✓ AGENTS.md
✓ UBS ignore
Files to Create
──────────────────────────────────────────────────────────
myapp/
├── .git/
├── AGENTS.md
├── .beads/
│ └── beads.db
├── .claude/
│ └── settings.local.json
├── .ubsignore
├── README.md
└── .gitignore
Options:
[Enter/c] Create project
[e] Edit selections (go back)
[q/Esc] CancelCLI mode (automation):
acfs newproj myapp
acfs newproj myapp /custom/path
acfs newproj myapp --no-brCLI mode's initial commit includes only the files it creates and known shareable Beads configuration files. It respects Git ignore rules, keeps Claude's local settings private, and leaves unrelated staged and untracked work out of the commit. If creation fails, the partial project stays on disk for inspection.
Reviewed bootstrap (plan first, then apply; needs Python 3 and runs no agents):
acfs newproj --guided # choose, review, save and create in the terminal
acfs newproj --plan myapp --preset first-project --stack python | tee myapp-plan.json
acfs newproj --plan myapp /custom/path --with git,readme,gitignore,agents
acfs newproj --apply myapp-plan.json --yes # create exactly what the plan lists
acfs newproj --apply myapp-plan.json --yes --resume # continue an interrupted apply--plan,--applyor--guidedmust come first; otherwise the arguments are read as CLI mode.--planprints the plan as JSON and creates nothing. Features are always explicit:--preset first-project(git, readme, gitignore, agents, starter, ci, prompt) or--witha comma-separated list of those plusbeadsandagent-mail.--stackispython(the default) ortypescript;--beadsadds local Beads initialization. The directory defaults to$ACFS_PROJECTS_DIR/<name>(/data/projects/<name>).--agent-mail URLalso registers the project with that Agent Mail service and configures project-local Claude MCP;--agent-mail-token-env NAMEreads the token from that variable without storing it, and--agent-mail-clients claude,codex,geminichooses which clients to configure.--applyregenerates the plan and refuses one that was edited or that this ACFS version would generate differently. It never overwrites existing files.
Opt-in GitHub publication and ru registration
To create a GitHub repository and publish the initial project, include both an explicit visibility
and an exact OWNER/REPO destination in the reviewed plan. Replace OWNER with your GitHub account or
organization. Add --ru if you also want repo_updater to manage the new repository:
acfs newproj --plan myapp --preset first-project --stack python \
--github=private --github-repo OWNER/myapp --ru > myapp-plan.json
# Inspect the complete plan, including the files that will be published.
cat myapp-plan.json
acfs newproj --apply myapp-plan.json --yesUse --github=public only when you intend to make those files public. Omitting --github preserves
local-only creation; configuration variables never enable publication or registration implicitly.
--github-repo is required with --github, and --ru requires GitHub publication. The selected preset
or --with list must include git. These options belong to the reviewed --plan workflow; the older
positional CLI and --interactive TUI remain local-only. acfs newproj --guided offers the same choices
with none as the GitHub default and a separate opt-in for ru.
Planning remains offline and creates nothing. The plan records repository visibility and destination,
the initial commit and push to main, the exact publication file list, and any ru configuration/state
directories that will be written. Applying with --yes, or choosing create after guided review,
authorizes those specific operations. It does not authorize later agent pushes; the generated
AGENTS.md keeps its explicit-permission requirement.
Before applying, install gh and authenticate for GitHub with permission to create the requested
repository (for example, gh auth login --hostname github.com). Install ru as well if you selected
--ru. Apply checks required tools, authentication, and an existing repository name before creating
the local project. An organization policy or a name claimed after preflight can still reject creation;
the error is reported and the local project is retained.
Only the plan's publication files enter the initial commit. Private recovery state, Agent Mail
connection settings, Beads runtime metadata, and unrelated files in the directory are excluded.
Publication never force-pushes. --ru registers the repository in the matching private/public list;
it does not run ru sync or change ru's projects directory or clone layout. In particular, a custom
new-project path is not a new ru path mapping: later syncs follow your existing ru configuration.
The plan captures the configuration and state directories from RU_CONFIG_DIR/RU_STATE_DIR, or their
XDG/home defaults, so a changed environment cannot silently redirect registration at apply time.
If a later step fails, keep the saved plan and project:
acfs newproj --apply myapp-plan.json --yes --resumeThe failure report identifies completed steps, whether the repository was created, and whether initial
publication is confirmed or still unconfirmed. A push failure keeps the new repository available for
retry; a registration failure keeps the published repository and retries ru registration. Resume reuses the recorded initial commit
and checks the repository identity, visibility, local files, Git configuration, remote, and HEAD
before continuing. It refuses changed or unrelated work instead of overwriting it. No repository or
project files are deleted as rollback.
If creation may have succeeded but its immutable GitHub repository ID was not durably saved, automatic resume stops for manual review. It will not adopt a repository merely because the name matches. Inspect the reported GitHub destination and complete any remaining operations explicitly in that case. This workflow creates new repositories on GitHub.com; it does not attach existing repositories or configure GitHub Enterprise hosts.
Notes:
- The TUI uses gum when available (arrow keys, Space to toggle, Enter to confirm). Without gum, it falls back to numbered prompts.
- Minimum terminal size: 60x15.
- CLI mode skips existing AGENTS.md; the wizard overwrites it, so move it aside if you want to keep the old one.
acfs agents — Flywheel Agent Guide
ACFS generates a machine-wide agent guide (installed tools with live versions, workflows, safety rules). The guide lives only in ACFS-owned storage, and ACFS refreshes it there on install/update:
~/.acfs/docs/flywheel-agent-guide.md # canonical, freely regenerated by ACFS
~/.acfs/docs/AGENTS.workspace.md # workspace AGENTS.md template (canonical copy)ACFS never automatically writes /AGENTS.md, ~/.codex/AGENTS.md, or a project's AGENTS.md — those files can contain user-authored rules and belong to you. (/data/projects/AGENTS.md is seeded once on a fresh install only when absent, and is never overwritten afterward.) Deploying the guide into a real instruction surface is an explicit step:
acfs agents update # regenerate the canonical guide
acfs agents path # print the canonical path
acfs agents install --codex-global # deploy to ~/.codex/AGENTS.md (Codex global scope)
acfs agents install --project DIR # deploy to DIR/AGENTS.md (project scope)
acfs agents install --workspace # deploy to /data/projects/AGENTS.md
acfs agents install --root # deploy to /AGENTS.md (legacy; not auto-discovered)
acfs agents install --to PATH # deploy anywhere elseDeployment creates the destination only when it is absent, including when another process creates it during deployment. If the destination already exists with different content, the deploy refuses, leaves your file untouched, and writes a merge candidate next to it (<dest>.acfs-new) so you can diff and merge manually. An existing different candidate is preserved and a unique suffixed candidate is reported. Redeploying identical content is an idempotent no-op.
acfs agents update --output PATH uses the same create-only rules. Only the canonical guide in ACFS-owned storage is automatically replaced. Publication requires Python 3, rejects symlink destinations and parent directories, and stages complete content beside the destination before publishing it. Interrupted staging files are retained for inspection.
Discovery scopes worth knowing: Codex reads global guidance from ~/.codex/AGENTS.md and project guidance from the project root down to the working directory; filesystem-root /AGENTS.md is not automatically read by any major harness, which is exactly why ACFS no longer writes it.
Tool detection always runs in the target user's context (including under sudo, resolved via SUDO_USER with ~/.local/bin, ~/go/bin, etc. on PATH), so user-local tools are reported accurately.
acfs info — System Overview
Quickly displays installation and onboarding state without verification or live probes by default.
acfs info # Terminal output (default)
acfs info --json # JSON output for scripting
acfs info --html # Self-contained HTML page
acfs info --minimal # Just essentials (IP, key commands)
acfs info --live # Include live coordination probes; can take longerExample output:
╔══════════════════════════════════════════════════════════════╗
║ ACFS System Info ║
╠══════════════════════════════════════════════════════════════╣
║ Host: vps-12345.contabo.net ║
║ IP: 192.168.1.100 ║
║ User: ubuntu ║
║ Uptime: 3 days, 4 hours ║
║ ║
║ Quick Commands: ║
║ cc → Claude Code (dangerous mode) ║
║ cod → Codex CLI (dangerous mode) ║
║ agy → Antigravity CLI (Gemini 4 Argon High) ║
║ ntm → Named Tmux Manager ║
╚══════════════════════════════════════════════════════════════╝Design Philosophy:
- Speed: Reads local state without waiting for coordination probes; live telemetry is opt-in
- Read-only by default: Reads installation/onboarding state and local interfaces without invoking coordination tools or writing caches. Swarm values remain unknown until live telemetry is requested
- Live telemetry:
--liveprobes the optional coordination tools, which may update their own local state. Dashboard generation requests this mode explicitly to populate its operations panel - Offline by default: No network calls; IP discovery reads local interfaces and routing tables only
- Legacy IP cache: A cached value is reused only after a bounded read confirms a recent ordinary file and a valid IPv4 or IPv6 address. Linked, malformed, oversized or stale caches fall back to local discovery without changing the cache. If Python 3 is unavailable, local discovery still works
- Fallback: Graceful degradation if data missing
acfs cheatsheet — Alias Discovery
Parses ~/.acfs/zsh/acfs.zshrc to show all installed aliases and commands.
acfs cheatsheet # List all aliases
acfs cheatsheet git # Filter by category or search term
acfs cheatsheet --category Agents
acfs cheatsheet --search docker
acfs cheatsheet --json # JSON output for toolingExample output:
╔═══════════════════════════════════════════════════════════════╗
║ ACFS Cheatsheet ║
╠═══════════════════════════════════════════════════════════════╣
║ Agents ║
║ cc → claude --dangerously-skip-permissions ║
║ cod → codex --dangerously-bypass-approvals-and-sandbox ║
║ agy → agy --model 'Gemini 4 Argon (High)' ║
║ ║
║ Git ║
║ gs → git status ║
║ gp → git push ║
║ gl → git pull ║
║ gco → git checkout ║
║ ║
║ Modern CLI ║
║ ls → lsd --inode --long --all ║
║ cat → bat ║
║ grep → rg ║
║ lg → lazygit ║
╚═══════════════════════════════════════════════════════════════╝acfs dashboard — HTML Status Page
Generates a self-contained HTML dashboard and optionally serves it.
acfs dashboard generate # Generate ~/.acfs/dashboard/index.html
acfs dashboard generate --force # Force regeneration
acfs dashboard serve # Serve on localhost:8080
acfs dashboard serve --port 3000 # Custom port
acfs dashboard serve --public # Bind to 0.0.0.0The default server accepts local connections. To view it from your laptop, use
the SSH tunnel command printed at startup, then open http://localhost:8080.
--public and a non-loopback --host expose the dashboard without authentication
to anyone who can reach the port, including its hostnames and install state.
Use an SSH tunnel on untrusted networks. The server publishes only index.html;
temporary files, timestamps, other files, and directory listings are not served.
The page and its parent directories must be real paths; linked paths are refused.
Python 3 is required for generation and HTTP serving; you can also open an existing HTML page directly.
Generation refuses linked or non-regular output files, including hardlinked freshness
timestamps. It stages the complete page and timestamp in a private .staging directory
before replacing them. Failed staging files are retained there for diagnosis; the error
prints their location. Rendering has a 60-second limit. A valid recent page is reused
without rewriting either file.
The dashboard provides:
- System health at a glance
- Tool versions and status
- Quick command reference
- Recent activity summary
acfs services — Background Daemon Management
Start, stop, restart, inspect, or follow logs for the local coordination daemons:
acfs services start
acfs services status
acfs services logs agent-mail
acfs services restart cass # restart one service; the others keep running
acfs services drift # is the live process on the installed binary?
acfs services stopAgent Mail keeps its ACFS-reserved 127.0.0.1:8765 endpoint and reuses the native
user service when one is already healthy. CM runs on 127.0.0.1:8766, and CM plus
the CASS watch indexer run in the acfs-svc tmux session. start and status
return nonzero if any daemon fails its runtime readiness check.
Lifecycle contract (the tmux tradeoff chosen in #196, made explicit per #360):
- The installer does not start this service group; run
acfs services startyourself after install (and after every reboot). startis a one-shot launcher, not a foreground supervisor: it brings the daemons up, reports readiness, and exits.- Only Agent Mail is normally boot-persistent, via its native systemd user
service. CM and the CASS watch indexer live in the
acfs-svctmux session, which does not survive a reboot and does not restart crashed processes -- after a reboot or crash, rerunacfs services start. - CM's HTTP server is optional if you only use
cm context/cm reflectfrom the CLI; those read the store directly. - Leaving the CASS watcher off can be deliberate (for example while diagnosing
indexing or resource problems);
acfs services statusreporting it "not running" is not necessarily a fault.
Converging a partly-down group (#383): when the acfs-svc session already
exists, start repairs it instead of reporting and exiting. It relaunches only
the services that are not running -- a pane whose process is alive is never
touched, which matters when the tmux server also hosts long-lived agent panes.
acfs services repair is the same operation under its own name.
Preflight before teardown (#382): restart resolves and validates every
binary it will need before it stops anything. Validation is the post-install
smoke check from #378 -- the binary must exist, be executable, and answer a
probe; a broken verdict (timeout, wrong architecture, missing loader, crash)
aborts the restart with no service-state change, while an unsupported verdict
(the CLI rejects --version/--help/version as unknown arguments) is
accepted. Managed binaries are resolved from the ACFS install directories as
well as PATH, so a noninteractive SSH shell without ~/.local/bin on PATH
no longer takes the group down. restart also accepts service names
(acfs services restart cass) to restart one service without interrupting the
others.
Running-binary drift (#381): updating a tool replaces the file on disk, but
a service keeps executing the inode it started with -- cass --version then
reports the new release while the live watcher still runs the old code. ACFS
compares what each service executes (/proc/<pid>/exe, which is not the same
thing as what PATH resolves) against the installed binary, and warns with both
paths and hashes from acfs services status, acfs doctor, and the end of
acfs update. By default nothing is restarted automatically: the warning names
the exact per-service restart command so you can pick a quiescent moment. To
have acfs update restart chosen services when, and only when, this check finds
them stale, list them in ACFS_UPDATE_RESTART_SERVICES (comma- or
space-separated, e.g. cm or cm,cass; Dicklesworthstone/agentic_coding_flywheel_setup#464). Each restart is confirmed with
the same check and reported; a listed name that is not a managed service is
reported and ignored. To limit it to the nightly run, set it in a drop-in:
systemctl --user edit acfs-nightly-update.service
# [Service]
# Environment=ACFS_UPDATE_RESTART_SERVICES=cmacfs services drift runs the check on demand (--robot for one
service|state|detail line per service). The comparison needs procfs, so it
reports unknown on platforms without one.
This lifecycle command is distinct from acfs services-setup, which configures
credentials and integrations rather than background processes.
acfs services-setup — Credential Configuration
Interactive wizard for configuring AI agent credentials and cloud service logins.
acfs services-setup # Run full setup wizardGuides you through:
- Claude Code: API key configuration
- Codex CLI: ChatGPT account login
- Antigravity CLI: Google account authentication
- GitHub CLI:
gh auth login - Cloud CLIs: Wrangler, Supabase, Vercel authentication
Also offers to install DCG (Destructive Command Guard), a Claude Code hook that blocks destructive commands like rm -rf /.
acfs continue — Upgrade Progress
After an Ubuntu upgrade reboot, view installation progress:
acfs continue # Show current upgrade statusDisplays:
- Original Ubuntu version
- Target version
- Current upgrade stage
- Next steps after completion
Learning Hub (Web)
In addition to the terminal-based onboarding, ACFS provides a comprehensive web-based Learning Hub at agent-flywheel.com/learn.
Web Lessons
The Learning Hub provides interactive lessons with progress tracking:
| # | Lesson | Duration | Topics |
|---|---|---|---|
| 0 | Welcome & Overview | 5 min | What's installed, mental model |
| 1 | Linux Navigation | 8 min | Filesystem structure, essential commands |
| 2 | SSH & Persistence | 6 min | Secure connections, staying connected |
| 3 | tmux Basics | 7 min | Sessions, windows, panes, survival |
| 4 | Git Essentials | 10 min | Version control, dangerous operations |
| 5 | GitHub CLI | 8 min | Issues, PRs, releases via gh |
| 6 | Agent Commands | 10 min | Claude, Codex, Antigravity usage |
| 7 | NTM Command Center | 8 min | Session orchestration |
| 8 | NTM Prompt Palette | 6 min | Quick command access |
| 9 | The Flywheel Loop | 10 min | How all 10 tools work together |
These are the core lessons; the hub ships 65 lessons in total (the core track plus per-tool deep dives such as UBS, Agent Mail, CASS, Beads, SLB, RCH and the case studies below), all defined in apps/web/lib/lessons.ts.
Features:
- Progress tracking in localStorage
- Code blocks with copy buttons
- Expandable deep-dive sections
- Practical exercises
Command Reference
The Command Reference documents every installed tool:
| Category | Commands |
|---|---|
| Agents | cc, cod, agy |
| Search | rg, fd, sg, fzf |
| Git | lg, gh, git-lfs |
| System | z, bat, lsd, atuin, tmux |
| Stack | ntm, am, br, bv, cass, cm, ubs, dcg, ru, rch, slb, caam |
| Languages | bun, uv, cargo, go |
| Cloud | wrangler, supabase, vercel, vault |
Technical Glossary
The Glossary defines 100+ technical terms with:
- One-liner: Quick tooltip definition
- Full explanation: Plain language description
- Analogy: "Think of it like..."
- Why we use it: Problem it solves
- Related terms: For context
Example entry:
RAM (Random Access Memory)
├── Short: Fast temporary storage your computer uses while working
├── Long: RAM is your computer's short-term memory...
├── Analogy: Like your desk space while working
├── Why: More RAM = run more programs simultaneously
└── Related: vCPU, VPS, NVMeFlywheel Visualization
The Flywheel page visualizes tool interactions:
Plan (Beads) ──> Coordinate (Agent Mail) ──> Execute (NTM + Agents)
^ │
│ v
└──── Remember (CASS Memory) <──── Scan (UBS) ┘Workflow Scenarios:
| Scenario | Description | Timeframe |
|---|---|---|
| Daily Parallel Progress | Keep several projects moving at once, even without the mental bandwidth for all of them | 3+ hours of autonomous work |
| Agents Reviewing Agents | Agents review each other's work before it becomes a problem | Continuous improvement loop |
| 5,500 Lines to 347 Beads | Turn a massive planning document into a dependency-tracked task graph | ~1 day for a complex feature |
| Fresh Eyes Code Review | Agents re-investigate code from a fresh perspective to find what humans miss | Continuous |
| Multi-Repo Morning Sync | Start the day with every repo synced and agents spawned across the fleet | < 10 minutes to full productivity |
| Bulk Commit Sweep | ru commit-sweep turns dirty worktrees into logical conventional commits (plan first, --execute to apply) | 30 min – 2 hours depending on repo count |
| Resource-Protected Agent Swarm | Run heavy agents without the workstation freezing (SRPS keeps priorities in check) | Hours of unattended work |
The scenarios are defined in apps/web/lib/flywheel.ts; the page renders whatever that file contains.
Tool Catalog (TL;DR page)
The TL;DR page is the searchable catalog of installed tools (the old /tools page was folded into it and now redirects there):
- Search & Filter: A search box (press
/to focus) filters tools by name, CLI command, or description - Tool Details: Each card shows the tool's tagline, its CLI command as a chip, and a copyable example invocation
- Synergy Diagram: An interactive diagram shows how the flywheel tools feed each other
- Live Data: Cards are generated from
acfs.manifest.yaml— manifest tools without a hand-curated entry are auto-appended so the page can never silently omit a tool
This page helps users discover tools they may not know about and understand how each fits into the agentic coding workflow.
Interactive Website Components
The wizard website includes specialized components for guiding beginners:
ConnectionCheck Component: A prominent visual that helps users verify they're connected to their VPS before running commands:
- Side-by-side comparison: "Wrong (laptop)" vs "Right (VPS)"
- Terminal prompt examples for Windows, Mac, and Linux
- Clear "STOP!" warning with color-coded styling
CommandCard Component: CLI instruction cards with:
- Syntax-highlighted code blocks
- One-click copy button
- Platform-specific variations (bash/zsh/PowerShell)
- Expandable explanations
Jargon Component (Responsive Technical Terms):
A tooltip system (apps/web/components/jargon.tsx) that adapts to the pointer type:
Desktop behavior:
- Hover or keyboard focus reveals a floating definition card, rendered through a portal so it escapes stacking contexts
- Viewport-aware positioning (auto-flips when near edges)
- A short close delay so the pointer can travel into the card
Mobile behavior:
- Tap opens a bottom sheet (
apps/web/components/ui/bottom-sheet.tsx, framer-motion) with the full definition - Swipe-to-dismiss from the handle, Escape and backdrop dismissal, focus returned to the term
- Body scroll locked while open and restored on close
Visual features:
- Dotted primary-colored underline marks a term as interactive
- Reduced-motion users get fades instead of slides
- Colors come from the OKLCH design tokens
Content structure per term:
{
term: "VPS",
short: "Virtual Private Server - a remote computer you rent",
long: "A VPS is your own slice of a powerful computer...",
analogy: "Think of it like renting an apartment in a building",
whyWeUseIt: "You get root access, dedicated resources...",
relatedTerms: ["SSH", "Ubuntu", "RAM"]
}Confetti Celebration: On lesson completion:
- Burst of celebratory confetti particles
- Randomized encouraging messages
- Special celebration for completing all lessons
- Respects
prefers-reduced-motionsetting
Stepper Component: Multi-step progress indicator:
- Visual step-by-step progress
- Clickable navigation
- Completion checkmarks
- Mobile-responsive design
Expanded Lesson Library
The Learning Hub includes specialized lessons for each tool in the Agent Flywheel stack:
| Lesson | Topics |
|---|---|
| UBS (Bug Scanner) | Scan workflow, severity levels, CI integration |
| Agent Mail | Registration, messaging, file reservations |
| CASS (Session Search) | Indexing, searching, cross-agent queries |
| CASS Memory (cm) | Rule extraction, playbook management |
| Beads | Issue tracking, graph metrics, priorities |
| SLB (Safety) | Two-person rule, dangerous command approval |
| Prompt Engineering | Effective prompts, context management |
| Real-World Case Study | End-to-end feature development walkthrough |
Each lesson includes:
- Conceptual introduction
- Practical commands with examples
- Interactive exercises
- Common pitfalls to avoid
- Links to tool documentation
Interactive Onboarding (TUI)
After installation, users can learn the ACFS workflow through an interactive terminal-based tutorial. The onboarding TUI discovers lesson markdown files dynamically from acfs/onboard/lessons, so the curriculum can grow as new tools and workflows are added without changing the launcher.
Running Onboarding
onboard # Launch interactive menu
onboard status # Show completion status
onboard --list # Alias for status
onboard 3 # Jump to lesson 3
onboard reset # Reset progress and start fresh
onboard --reset # Alias for resetLessons
Run onboard --help to see the currently discovered lesson list. The curriculum currently spans Linux basics, SSH, tmux, agent login, NTM, the flywheel workflow, updating, Beads, RCH, and other ACFS tools. Because lessons are discovered by filename, adding a new NN_name.md file automatically extends the tutorial.
Progress Tracking
Progress is saved in ~/.acfs/onboard_progress.json:
{
"completed": [0, 1, 2],
"current": 3,
"started_at": "2024-12-20T10:30:00-05:00"
}The TUI shows completion status for each lesson and suggests the next one to take. Users can jump to any lesson or re-take completed ones.
Enhanced UX with Gum
If Charmbracelet Gum is installed, the onboarding system uses it for enhanced terminal UI—selection menus, styled prompts, and better formatting. Without Gum, it falls back to simple numbered menus that work everywhere.
Tools Installed
ACFS installs a comprehensive suite of 30+ tools organized into categories:
Shell & Terminal UX
| Tool | Command | Description |
|---|---|---|
| zsh | zsh | Modern shell |
| oh-my-zsh | - | zsh plugin framework |
| powerlevel10k | - | Fast, customizable prompt |
| lsd | ls (aliased) | Modern ls with icons |
| atuin | atuin search | Searchable shell history database (its zsh hook is intentionally not enabled; Ctrl+R is the shell's own history search) |
| fzf | fzf | Fuzzy finder |
| zoxide | z | Smarter cd |
| direnv | - | Directory-specific env vars |
Languages & Package Managers
| Tool | Command | Description |
|---|---|---|
| bun | bun | Fast JS/TS runtime + package manager |
| uv | uv | Fast Python package manager |
| Rust | cargo | Rust toolchain |
| Go | go | Go toolchain |
Dev Tools
| Tool | Command | Description |
|---|---|---|
| tmux | tmux | Terminal multiplexer |
| ripgrep | rg | Fast recursive grep |
| ast-grep | sg | Structural code search |
| lazygit | lg (aliased) | Git TUI |
| GitHub CLI | gh | GitHub auth, issues, PRs |
| Git LFS | git-lfs | Large file support for Git |
| bat | cat (aliased) | Cat with syntax highlighting |
| neovim | nvim | Modern vim |
| jq | jq | JSON processor |
| rsync | rsync | Fast file sync/copy |
| lsof | lsof | Debug open files/ports |
| dnsutils | dig | DNS debugging |
| netcat | nc | Network debugging |
| strace | strace | Syscall tracing |
| minisign | minisign | Release signature verification (required by the Agent Mail and CAAM installers) |
Networking
| Tool | Command | Description |
|---|---|---|
| Tailscale | tailscale | Zero-config mesh VPN |
Tailscale Integration:
Tailscale provides secure, encrypted networking between your devices without complex firewall configuration:
# Authenticate and join your tailnet
tailscale up
# Check connection status
tailscale status
# Get your Tailscale IP
tailscale ip
# SSH over Tailscale (bypasses firewalls)
ssh ubuntu@your-vps.tailnet-name.ts.netBenefits for agentic workflows:
- Firewall-free access: Connect even when behind NAT or restrictive firewalls
- MagicDNS: Access your VPS by hostname instead of IP
- SSH keys over Tailscale: Use
tailscale sshfor key-free authentication - ACLs: Fine-grained access control for team environments
Compatible AI Coding Agents
ACFS ships 7 coding-agent modules; 3 of them install by default.
| Agent | CLI | ACFS aliases | Install | Module | Sign in | Docs |
|---|---|---|---|---|---|---|
| Antigravity CLI (Google) | agy | agy, gmi | Default | agents.antigravity | agy | docs |
| Claude Code (Anthropic) | claude | cc | Default | agents.claude | claude auth login | docs |
| Codex CLI (OpenAI) | codex | cod | Default | agents.codex | codex login --device-auth | docs |
| Grok CLI (xAI) | grok | — | Optional | agents.grok | grok login | docs |
| oh-my-pi | omp | — | Optional | agents.omp | omp auth-broker login <provider> | docs |
| OpenCode | opencode | — | Optional | agents.opencode | opencode auth login | docs |
| Gemini CLI (Google) | gemini | — | Legacy | agents.gemini | gemini | docs |
Turn any of them on or off at install time with the Module column above: --only <module> installs that agent plus its dependencies, --skip <module> leaves it out — for example --only agents.grok or --skip agents.codex. --list-modules prints every module id and --print-plan shows what a given selection would run.
What each one is for:
- Antigravity CLI — Google's successor to the Gemini CLI; ACFS pins its model and permissions via agy-locked.
- Claude Code — Long autonomous runs with deep tool use; the ACFS default driver.
- Codex CLI — Runs on a ChatGPT plan; --device-auth is the login path on a headless VPS.
- Grok CLI — xAI's terminal agent; also accepts GROK_DEPLOYMENT_KEY for headless use.
- oh-my-pi — Community fork of the Pi agent with its own model roster and credential broker.
- OpenCode — Multi-provider harness; drives Claude, GPT, and Gemini models from one TUI.
- Gemini CLI — Retired upstream on 2026-06-18; kept installable for existing setups, use Antigravity instead.
Vibe Mode Aliases:
# Claude Code with max memory (background tasks enabled by default)
alias cc='NODE_OPTIONS="--max-old-space-size=32768" claude --dangerously-skip-permissions'
# Codex with bypass and dangerous filesystem access
alias cod='codex --dangerously-bypass-approvals-and-sandbox --search -m gpt-6-astra -c model_reasoning_effort=xhigh -c model_reasoning_summary_format=experimental'
# Antigravity CLI, model/settings/DCG locked by the ACFS launcher
alias agy='$HOME/.local/bin/agy-locked'
alias gmi='$HOME/.local/bin/agy-locked'Installation & Updates: Claude Code should be installed and updated using its native mechanisms:
- Install: ACFS uses the official native installer (
claude.ai/install.sh), checksum-verified viachecksums.yaml(installs to~/.local/bin/claude) - Update: Use
claude update --channel latest(built-in) or runacfs update --agents-only
This ensures proper authentication handling and avoids issues with alternative package manager builds. ACFS updates Codex with Bun global package updates, Antigravity with its native agy update path, oh-my-pi with its native omp update path, and Grok CLI by re-running its checksum-verified installer.
Cloud & Database
| Tool | Command | Description |
|---|---|---|
| PostgreSQL 18 | psql | Database |
| HashiCorp Vault | vault | Secrets management |
| Wrangler | wrangler | Cloudflare CLI |
| Supabase CLI | supabase | Supabase management |
| Vercel CLI | vercel | Vercel deployment |
Vault is installed by default (skip with --skip-vault). ACFS installs the Vault CLI so you have a real secrets tool available early; it does not automatically configure a Vault server for you.
Supabase networking note: some Supabase projects expose the direct Postgres host over IPv6-only (often on free tiers). If your VPS/network is IPv4-only, use the Supabase pooler connection string instead (or upgrade/configure networking for direct IPv4).
Agent Flywheel Stack (Core Tools)
The core suite of tools for professional agentic workflows:
| # | Tool | Command | Description |
|---|---|---|---|
| 1 | Named Tmux Manager | ntm | Agent cockpit—spawn, orchestrate, monitor tmux sessions |
| 2 | MCP Agent Mail | am | Agent coordination via mail-like messaging (Rust rewrite) |
| 3 | BeadsRust | br | Dependency-aware issue tracker for agents (Rust implementation) |
| 4 | Beads Viewer | bv | Task management TUI with graph analysis |
| 5 | Coding Agent Session Search | cass | Unified agent history search (CASS) |
| 6 | CASS Memory System | cm | Procedural memory for agents |
| 7 | Ultimate Bug Scanner | ubs | Bug scanning with guardrails |
| 8 | Destructive Command Guard | dcg | Claude Code hook blocking dangerous git/fs commands |
| 9 | Repo Updater | ru | Multi-repo sync + AI-driven commit automation |
| 10 | Remote Compilation Helper | rch | Transparent build offloading to faster machines |
| 11 | Coding Agent Account Manager | caam | Agent auth switching |
| 12 | Simultaneous Launch Button | slb | Two-person rule for dangerous commands |
Bundled Utilities
Additional productivity tools installed alongside the stack:
| Tool | Command | Description |
|---|---|---|
| Get Image from Internet Link | giil | Download images from iCloud, Dropbox, Google Photos for visual debugging |
| Chat Shared Conversation to File | csctf | Convert AI share links (ChatGPT, Gemini, Claude) to Markdown/HTML |
Doctor Command
acfs doctor performs comprehensive health checks on your installation:
$ acfs doctor
╔══════════════════════════════════════════════════════════════╗
║ ACFS Health Check ║
╠══════════════════════════════════════════════════════════════╣
║ Identity ║
║ ✔ Running as ubuntu user ║
║ ✔ Passwordless sudo enabled ║
║ ║
║ Workspace ║
║ ✔ /data/projects exists ║
║ ║
║ Shell ║
║ ✔ zsh installed ║
║ ✔ oh-my-zsh installed ║
║ ✔ powerlevel10k installed ║
║ ✔ acfs.zshrc sourced ║
║ ║
║ Core Tools ║
║ ✔ bun 1.2.16 ║
║ ✔ uv 0.5.14 ║
║ ✔ cargo 1.84.0 ║
║ ✔ go 1.23.4 ║
║ ✔ ripgrep 14.1.0 ║
║ ✔ ast-grep 0.30.1 ║
║ ║
║ Agents ║
║ ✔ claude 1.0.24 ║
║ ✔ codex 0.1.2504252326 ║
║ ✔ agy 1.0.12 ║
║ ║
║ Cloud ║
║ ✔ vault 1.18.3 ║
║ ✔ wrangler 4.16.0 ║
║ ✔ supabase 2.23.4 ║
║ ✔ vercel 41.7.6 ║
║ ║
║ Agent Flywheel Stack ║
║ ✔ ntm 0.3.2 ║
║ ✔ slb 0.2.1 ║
║ ✔ ubs 0.1.8 ║
║ ✔ bv 0.9.4 ║
║ ✔ cass 0.4.2 ║
║ ✔ cm 0.1.3 ║
║ ✔ caam 0.2.0 ║
║ ✔ dcg 0.1.0 ║
║ ✔ ru 1.2.0 ║
║ ⚠ mcp_agent_mail (not running) ║
║ ║
║ Utilities ║
║ ✔ giil 3.0.0 ║
║ ✔ csctf 1.0.0 ║
╠══════════════════════════════════════════════════════════════╣
║ Overall: 35/36 checks passed ║
╚══════════════════════════════════════════════════════════════╝Generated Doctor Checks
Doctor checks are generated from the manifest (scripts/generated/doctor_checks.sh) to keep verification logic close to acfs.manifest.yaml. The acfs doctor command automatically sources these generated checks to verify all manifest-defined tools.
How it works:
- The manifest generator creates
doctor_checks.shwith verify commands for each module acfs doctorsources this file and runs each verification check- Failed checks display a fix suggestion with the exact command to reinstall
Example output with fix suggestion:
✗ tools.lazygit - Lazygit terminal UI not found
Fix: curl -fsSL https://agent-flywheel.com/install | bash -s -- --yes --force-reinstall --only tools.lazygitThis architecture ensures doctor checks stay in sync with the installer—if a tool is in the manifest, it will be verified.
Options
acfs doctor # Interactive colorful output
acfs doctor --json # Machine-readable JSON output
acfs doctor --quiet # Exit code only (0=healthy, 1=issues)
acfs doctor --deep # Run functional tests (auth, connections)
acfs doctor --fix # Apply safe fixes for failed checks
acfs doctor --dry-run # Preview fixes without applying
acfs doctor --no-cache # Skip cache, run all checks freshDeep Checks (--deep)
The --deep flag runs functional tests beyond binary existence:
| Category | Checks |
|---|---|
| Agent Auth | Claude config, Codex OAuth, Antigravity credentials |
| Database | PostgreSQL connection, ubuntu role exists |
| Cloud CLIs | gh auth status, wrangler whoami, Supabase/Vercel tokens |
| Vault | VAULT_ADDR configured |
Deep checks use 15-second timeouts to avoid hanging on network issues. Successful results are cached for 5 minutes to speed up repeated runs.
Example output:
Deep Checks
✔ Claude auth configured
✔ PostgreSQL connection working
⚠ Codex not authenticated (run: codex login)
✔ GitHub CLI authenticated
8/9 functional tests passed in 3.2sAuto-Fix Mode (--fix)
The --fix flag automatically applies safe, deterministic fixes for common issues:
acfs doctor --fix # Apply safe fixes
acfs doctor --fix --dry-run # Preview fixes without applyingSafe Auto-Fixers
These fixes are applied when --fix is used. Failed checks are fixed right away; warning-level checks (which most of these are) additionally need --yes, so the usual invocation is acfs doctor --fix --yes:
| Fix ID | Description | Undo Strategy |
|---|---|---|
fix.path.ordering | Prepend ACFS directories to PATH in .zshrc | Restore backup |
fix.config.copy | Copy missing ~/.acfs config files | Remove copied file |
fix.dcg.hook | Install DCG pre-tool-use hook | Run dcg uninstall |
fix.symlink.create | Create missing tool symlinks | Remove symlink |
fix.plugin.clone | Clone missing zsh plugins | Remove cloned directory |
fix.acfs.sourcing | Add ACFS sourcing to .zshrc | Restore backup |
Safety Guarantees
- Never deletes user files — Only creates, modifies, or symlinks
- Backups before modify — SHA256-verified backups of all modified files
- Idempotent — Safe to run multiple times
- Logged — All changes recorded to
~/.local/share/acfs/doctor.log - Reversible — Configuration-level fixes (PATH, sourcing, config copies, symlinks, plugin clones) record an undo command; tool installs are not auto-reverted
Example Dry-Run Output
DRY-RUN: acfs doctor --fix
Would apply the following fixes:
[fix.path.ordering]
Action: Prepend PATH directories to ~/.zshrc
File: ~/.zshrc
Backup: Yes (SHA256 verified)
[fix.acfs.sourcing]
Action: Add ACFS sourcing to .zshrc
File: ~/.zshrc
Backup: Yes (SHA256 verified)
Fixes that require manual action:
[shell.ohmyzsh]
Status: FAIL
Suggestion: curl -fsSL https://install.ohmyz.sh/ | bash
Summary: 2 auto-fixes, 0 prompted, 1 manualManual-Only Fixes
Some operations are never auto-fixed and instead provide suggestions:
- Package manager operations (
apt install ...) - Anything requiring sudo
- File deletions
- Complex shell configuration changes
Undoing Changes
All changes made by --fix can be undone:
acfs undo --list # List all changes
acfs undo chg_0001 # Undo specific change
acfs undo --all # Undo all changes from the most recent fix session
acfs undo --everything # Undo every recorded change across all sessionsUndo restores the whole-file backup taken just before the fix ran. Any edits you
made to that file after the fix are reverted too, so copy them aside before undoing a
fix to a file you have since changed. Tool installs performed by --fix record no
automatic rollback; remove an unwanted tool manually.
The Wizard Website
The wizard guides beginners through a 13-step journey from "I have a laptop" to "AI agents are coding for me":
┌─────────────────────────────────────────────────────────────────────────────┐
│ ACFS Wizard [Step 3/13] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ STEP 3: Generate SSH Key │ │
│ │ ────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ Run this command in your terminal: │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ ssh-keygen -t ed25519 -C "your-email@example.com" [📋] │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ☐ I ran this command │ │
│ │ │ │
│ │ [← Previous] [Next Step →] │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ Progress: ●●●○○○○○○○○○○ │
└─────────────────────────────────────────────────────────────────────────────┘Wizard Steps
| Step | Title | What Happens |
|---|---|---|
| 1 | Choose Your OS | Select Mac, Windows, or Linux (auto-detected) |
| 2 | Install Terminal | Get a proper terminal application set up |
| 3 | Generate SSH Key | Create an ed25519 key for VPS access |
| 4 | Rent a VPS | Choose a VPS provider and plan |
| 5 | Create VPS Instance | Launch your VPS and confirm SSH access |
| 6 | SSH Into Your VPS | First connection with troubleshooting tips |
| 7 | Set Up Accounts | Create accounts for the services you'll use |
| 8 | Pre-Flight Check | Verify your VPS is ready before installing |
| 9 | Run Installer | The curl | bash one-liner |
| 10 | Reconnect as Ubuntu | Post-install reconnection |
| 11 | Verify Key Connection | Reconnect using your SSH key and confirm it works |
| 12 | Status Check | Run acfs doctor to verify |
| 13 | Launch Onboarding | Start the interactive tutorial |
Key Features
- OS Detection: Auto-detects Mac vs Windows for tailored instructions
- Copy-to-Clipboard: One-click copy for all commands
- Handoff Runbook: Downloadable JSON/Markdown artifact for the installer command and recovery steps
- Progress Tracking: localStorage persistence across browser sessions
- Confirmation Checkboxes: "I ran this command" acknowledgments
- Troubleshooting: Expandable help for common issues
Local learning recommendations
The Learning Hub suggests up to three useful next steps from the lessons completed in your browser and your selected stack. Each suggestion explains its reason. The next guided lesson respects sequential access; tool references are always available. The goal selector can prioritize installation recovery or coordination with multiple agents. These choices and the recommendations do not prove that tools are installed or authenticated.
With a valid saved wizard target, the existing local doctor JSON reader appears on the hub. Use the recovery goal to reach setup guidance if no target is saved. Findings or coverage gaps prioritize a diagnostic action. Only an in-memory boolean reaches the recommender; report contents, private identifiers and raw fix commands are never persisted or sent to analytics. Clearing/replacing the report, changing installation context or leaving the page clears that signal. Recommendations do not run commands, mark lessons complete, change your stack selection or unlock gated lessons. Unknown selections prompt review; new users and completed courses retain useful reference links without requiring a backend.
Wizard Handoff Runbook
Step 9 can download a local handoff runbook in JSON or Markdown. The runbook is meant for the user and support loop when an SSH session drops, an install is interrupted, or the user needs to remember the exact command they ran.
Schema: acfs.handoff-runbook.v1
| Field | Purpose |
|---|---|
wizardSelections | Local OS, install mode, source ref, and normalized target user |
targetHost | Redacted host kind and target-host assumptions |
ssh | Expected SSH key paths and redacted reconnect command templates |
install | Exact installer command generated from the shared command builder |
recoveryCommands | Copy/paste-safe reconnect, retry, doctor, and support-bundle commands |
support | Deterministic acfs support-bundle reference and review artifacts |
privacy | Explicit redaction policy for host data and exact-command inclusion |
Host addresses are not written into the runbook. The installer command stays exact so a user can paste it back into the VPS session, and the artifact points to support-report.md and manifest.json for support-bundle review.
Technology Stack
Next.js 16 (App Router)
├── React 19
├── Tailwind CSS 4 (OKLCH colors)
├── shadcn/ui components
├── Radix UI primitives
└── Lucide iconsNo backend required. All state is stored in:
- URL query parameters
- localStorage (
agent-flywheel-user-os,agent-flywheel-vps-ip,agent-flywheel-wizard-completed-steps)
Wizard State Management
The wizard uses TanStack Query for state management with optimistic updates and cross-tab synchronization:
Architecture:
// Query-based state with localStorage persistence
const { data: steps } = useQuery({
queryKey: ['wizardSteps', 'completed'],
queryFn: getCompletedSteps, // Reads from localStorage
staleTime: 0, // Always check for updates
gcTime: Infinity, // Never garbage collect
});Optimistic Updates with Rollback:
const mutation = useMutation({
mutationFn: async (stepId) => {
const newSteps = addCompletedStep(currentSteps, stepId);
setCompletedSteps(newSteps); // Persist to localStorage
return newSteps;
},
onMutate: (stepId) => {
// Optimistically update cache immediately
const previousSteps = queryClient.getQueryData(queryKey);
queryClient.setQueryData(queryKey, addCompletedStep(baseSteps, stepId));
return { previousSteps }; // For rollback
},
onError: (_err, _stepId, context) => {
// Rollback on failure
queryClient.setQueryData(queryKey, context.previousSteps);
},
});Cross-Tab Synchronization: The wizard maintains sync across browser tabs via two mechanisms:
- Custom DOM events for same-tab coordination between components
- Storage events for cross-tab updates when localStorage changes
// Same-tab: custom event dispatch
window.dispatchEvent(new CustomEvent('acfs:wizard:completed-steps-changed', {
detail: { steps }
}));
// Cross-tab: storage event listener
window.addEventListener('storage', (event) => {
if (event.key === COMPLETED_STEPS_KEY) {
queryClient.setQueryData(queryKey, getCompletedSteps());
}
});Safe localStorage Utilities: All localStorage access is wrapped in safe utilities that handle SSR, private browsing, and quota exceeded errors:
// Safe read (returns null on any error)
export function safeGetJSON<T>(key: string): T | null;
// Safe write (returns boolean success)
export function safeSetJSON(key: string, value: unknown): boolean;
// URL preservation for state fallback
export function withCurrentSearch(path: string): string;This architecture ensures the wizard progress survives browser refreshes, works across tabs, and degrades gracefully when localStorage is unavailable.
Configuration Files
ACFS deploys optimized configuration files to ~/.acfs/ on the target VPS.
~/.acfs/zsh/acfs.zshrc
A comprehensive zsh configuration that's sourced by ~/.zshrc:
Prompt resource limits on swarm hosts:
ACFS defaults GITSTATUS_NUM_THREADS to 2 worker threads before loading
Powerlevel10k. This bounds each new prompt daemon's worker pool on hosts with
many tmux panes. A normal interactive terminal retains its Git prompt; each
pane can still have its own daemon. An explicit nonempty value is preserved:
# In ~/.zshrc, before sourcing the ACFS configuration:
GITSTATUS_NUM_THREADS=4
source "$HOME/.acfs/zsh/acfs.zshrc"Prompt themes are initialized only for interactive terminal sessions with TTY
input and output. Command-mode shells such as zsh -lic 'command', including
those running in a PTY, skip Powerlevel10k and the fallback Starship prompt.
The agent markers CODEX_REMOTE_PAYLOAD, SIFS_AGENT, and AGENT_ENV also
suppress theme initialization when nonempty. The existing VS Code plain
prompt is selected before any heavy theme starts. Paths, aliases, and ACFS
commands remain available in these shells.
The thread setting applies when a daemon starts. Open a new shell or tmux pane after updating; ACFS does not terminate existing prompt daemons. A user theme loaded separately before ACFS remains under that user's control.
Oh-My-Zsh Plugins (14 total):
| Plugin | Category | What It Provides |
|---|---|---|
git | VCS | 150+ git aliases (gs, gp, gl, gco, gcm, etc.) |
sudo | Shell | Double-tap Esc to prefix previous command with sudo |
colored-man-pages | Shell | Colorized man pages for better readability |
command-not-found | Shell | Suggests packages when command not found |
docker | Containers | Docker command completion and aliases |
docker-compose | Containers | docker-compose completion and aliases |
python | Lang | Python aliases (pyfind, pyclean, pygrep) |
pip | Lang | pip completion and cache management |
tmux | Terminal | tmux aliases (ta, tad, ts, tl, tkss) |
tmuxinator | Terminal | tmuxinator project completion |
systemd | System | systemctl aliases (sc-status, sc-start, sc-stop) |
rsync | Tools | rsync completion and common flag aliases |
zsh-autosuggestions | UX | Fish-like autosuggestions from history |
zsh-syntax-highlighting | UX | Real-time command syntax highlighting |
Note:
zsh-autosuggestionsandzsh-syntax-highlightingare custom plugins installed from GitHub. They must be listed last for optimal performance.
Path Configuration:
export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export PATH="$HOME/go/bin:$PATH"
export PATH="$HOME/.bun/bin:$PATH"Atuin is still installed, but ACFS keeps it behind the guarded ~/.local/bin/atuin
shim instead of putting ~/.atuin/bin at the front of interactive shell PATH.
Modern CLI Aliases:
alias ls='lsd --inode --long --all'
alias ll='lsd -l'
alias tree='lsd --tree'
alias cat='bat'
alias grep='rg'
alias vim='nvim'
alias lg='lazygit'Tool Integrations:
# Zoxide (smarter cd)
eval "$(zoxide init zsh)"
# direnv (directory env vars)
eval "$(direnv hook zsh)"
# fzf (fuzzy finder)
source /usr/share/doc/fzf/examples/key-bindings.zshShell Keybindings (Quality of Life):
| Keybind | Action | Notes |
|---|---|---|
Ctrl+→ | Forward word | Navigate by word |
Ctrl+← | Backward word | Navigate by word |
Alt+→ | Forward word | Alternative binding |
Alt+← | Backward word | Alternative binding |
Ctrl+Backspace | Delete word backward | Fast deletion |
Ctrl+Delete | Delete word forward | Fast deletion |
Home | Beginning of line | Works in all terminals |
End | End of line | Works in all terminals |
Ctrl+R | Shell history search | Uses the active shell/editor binding |
Atuin History Bindings:
ACFS intentionally does not enable Atuin's zsh preexec/precmd integration by
default. Atuin's searchable CLI remains available as atuin search, but the
automatic shell hook can record every coding-agent command and grow the Atuin
database fast enough to make shells laggy.
~/.acfs/tmux/tmux.conf
A tmux configuration specifically optimized for NTM and multi-agent workflows:
Key Bindings:
Prefix: Ctrl+a (not Ctrl+b - more ergonomic)
Split horizontal: | (preserves working directory)
Split vertical: - (preserves working directory)
Navigate panes: h/j/k/l (vim-style)
Resize panes: H/J/K/L (repeatable with -r flag)
Reload config: r
New window: c (preserves working directory)Copy Mode (vim-style):
Enter copy mode: prefix + [
Begin selection: v
Rectangle selection: r
Copy and exit: yAgent Workflow Optimizations:
| Setting | Value | Purpose |
|---|---|---|
history-limit | 50,000 | Extended scrollback for long agent sessions |
escape-time | 10ms | Faster key response (reduced from default 500ms) |
focus-events | on | Enables vim/neovim autoread in agent windows |
detach-on-destroy | off | NTM compatibility—don't detach when session ends |
monitor-activity | on | Track agent window activity |
visual-activity | off | Silent monitoring (no bell) |
Catppuccin-Inspired Theme:
# Status bar (top position, less intrusive)
status-style: bg=#1e1e2e, fg=#cdd6f4
# Session indicator (blue accent)
status-left: #[fg=#89b4fa,bold] #S
# Active window highlight (pink accent)
window-status-current-format: #[fg=#f5c2e7,bold] #I:#W
# Pane borders
pane-border-style: fg=#313244
pane-active-border-style: fg=#89b4fa # Blue highlightLocal Overrides:
The config sources ~/.tmux.conf.local if it exists, allowing personal customizations without modifying ACFS defaults.
Library Modules
The installer is organized into modular Bash libraries in scripts/lib/:
logging.sh
Colored console output utilities:
log_step "1/8" "Installing packages..." # Blue step indicator
log_detail "Installing zsh..." # Gray indented detail
log_success "Complete" # Green checkmark
log_warn "May take a while" # Yellow warning
log_error "Failed" # Red error
log_fatal "Cannot continue" # Red error + exit 1security.sh
HTTPS enforcement and checksum verification:
enforce_https "$url" # Fail if not HTTPS
verify_checksum "$url" "$sha256" "$name" # Verify before execute
fetch_and_run "$url" "$sha256" "$name" # Verify + execute in oneos_detect.sh
OS detection and validation:
detect_os() # Sets OS_ID, OS_VERSION, OS_CODENAME
validate_os() # Checks supported Ubuntu or Arch-family releases
is_fresh_vps() # Heuristic detection of fresh VPS
get_arch() # Returns amd64/arm64
is_wsl() # Detects WSL
is_docker() # Detects Docker containeruser.sh
User account normalization:
ensure_user() # Creates ubuntu user if missing
enable_passwordless_sudo() # Adds NOPASSWD to sudoers
migrate_ssh_keys() # Copies keys from root to ubuntu
normalize_user() # Full normalization sequenceupdate.sh
Component update logic with version tracking and logging:
update_apt() # apt update/upgrade with lock detection
update_bun() # bun upgrade with version tracking
update_agents() # Claude, Codex, Antigravity, omp, Grok (version before/after)
update_cloud() # Wrangler, Supabase, Vercel (Supabase uses verified release tarball)
update_rust() # rustup update stable
update_uv() # uv self update
update_go() # Go toolchain update
update_shell() # OMZ, P10K, plugins, Atuin, Zoxide
update_stack() # Agent Flywheel stack tools
# Features:
# - Automatic logging to ~/.acfs/logs/updates/
# - Version tracking (before/after for each tool)
# - APT lock detection and warning
# - Reboot-required detection for kernel updates
# - Dry-run mode with --dry-run flaggum_ui.sh
Enhanced terminal UI using Charmbracelet Gum:
print_banner() # ASCII art ACFS banner
gum_step/gum_detail # Styled output
gum_success/warn/error # Colored messages
gum_spin # Spinner for long operations
gum_confirm # Yes/No prompt
gum_choose # Selection menuFalls back to basic echo if Gum is not installed.
error_tracking.sh
Sophisticated error collection and reporting:
track_error "phase" "step" "error_message"
track_warning "phase" "step" "warning_message"
get_error_report # Generate structured error report
get_error_count # Count of tracked errors
has_errors # Boolean check for any errorsFeatures:
- Collects errors without aborting execution
- Associates errors with phase and step context
- Generates end-of-run summary reports
- Distinguishes warnings from errors
state.sh
State machine management for installation progress (v3 schema):
state_init # Initialize state file
state_get_phase # Current phase
state_set_phase "phase_name" # Set current phase
state_mark_complete "phase_name" # Mark phase complete
state_has_completed "phase_name" # Check if phase done
state_save # Persist to disk (atomic)
state_load # Load from diskThe state file (~/.acfs/state.json) uses atomic writes to prevent corruption.
contract.sh
Runtime contract validation for generated scripts:
acfs_require_contract "module:${module_id}" # Assert module environment is ready
acfs_check_contract # Non-fatal contract checkValidates that required environment variables and functions exist before execution:
TARGET_USER,TARGET_HOME,MODEACFS_BOOTSTRAP_DIR,ACFS_LIB_DIR- Logging functions:
log_detail,log_success, etc.
smoke_test.sh
Standalone post-install verification. The installer itself runs its own inline
run_smoke_test (8 critical checks, defined in install.sh) at the end of an
install; scripts/lib/smoke_test.sh is the larger, separately tested version
of the same checks and is not invoked by the installer:
run_smoke_test # Execute all smoke testsCritical Checks (must pass):
- Running as ubuntu user
- Passwordless sudo enabled
- Zsh is default shell
- Core tools accessible (bun, uv, cargo)
Non-Critical Checks (warnings only):
- Agent authentication configured
- Cloud CLIs authenticated
- Optional tools installed
Example output:
[Smoke Test]
✅ Running as ubuntu user
✅ Passwordless sudo enabled
✅ Zsh is default shell
✅ bun --version works
⚠️ Codex not authenticated (run: codex login)
✅ 8/9 checks passedsession.sh
Agent session export functionality for sharing and replay:
session_export "claude-code" "session_id" "/output/path"
session_list # List exportable sessions
session_validate "/export/file.json"Implements the Session Export Schema for cross-agent sharing:
interface SessionExport {
schema_version: 1;
exported_at: string; // ISO8601
session_id: string;
agent: "claude-code" | "codex" | "agy";
model: string;
summary: string;
duration_minutes: number;
stats: {
turns: number;
files_created: number;
files_modified: number;
commands_run: number;
};
outcomes: Array<{
type: "file_created" | "file_modified" | "command_run";
path?: string;
description: string;
}>;
key_prompts: string[]; // Notable prompts for learning
sanitized_transcript: Array<{
role: "user" | "assistant";
content: string;
timestamp: string;
}>;
}tailscale.sh
Zero-config VPN setup for secure remote access:
install_tailscale # Install via official APT repo
verify_tailscale # Check installation
tailscale_status # Get connection statusTailscale provides:
- Secure mesh networking between your devices
- SSH over Tailscale for firewall-free access
- MagicDNS for hostname-based addressing
- ACL-based access control
After installation, run tailscale up to authenticate and join your tailnet.
ubuntu_upgrade.sh
Multi-reboot Ubuntu version upgrade automation, invoked only when an upgrade is explicitly requested:
start_ubuntu_upgrade # Begin upgrade chain
check_upgrade_status # Current upgrade state
resume_upgrade_after_reboot # Continue after rebootHandles the complex multi-step Ubuntu upgrade process:
- Detects current version
- Calculates a supported LTS upgrade path (e.g., 24.04 → 26.04)
- Performs sequential
do-release-upgradeoperations - Installs systemd service for post-reboot resume
- Continues ACFS installation after reaching target
MCP Agent Mail Integration
ACFS includes integration with MCP Agent Mail for multi-agent coordination:
What Agent Mail Provides
- Identities: Each agent registers with a unique name
- Inbox/Outbox: Message-based communication between agents
- File Reservations: Advisory leases to prevent agents from clobbering each other's work
- Searchable Threads: Full-text search across all messages
- Git Persistence: All artifacts stored in git for human auditability
Port 8765 is reserved for Agent Mail
ACFS runs Agent Mail as a user service bound to 127.0.0.1:8765 and health-checks it there.
cm serve (CASS Memory's MCP HTTP server) defaults to the same address, so on an ACFS machine
start it on another port (cm serve --port 8766, or MCP_HTTP_PORT=8766 cm serve); otherwise
whichever process starts second fails to bind and MCP clients can end up talking to the wrong
server. If Agent Mail cannot start, the installer reports which process is holding the port.
Core Patterns
1. Register Identity:
# In your agent, call:
mcp.ensure_project(project_key="/data/projects/my-project")
mcp.register_agent(project_key=..., program="claude-code", model="opus-4.5")2. Reserve Files Before Editing:
mcp.file_reservation_paths(
project_key=...,
agent_name="BlueLake",
paths=["src/**"],
ttl_seconds=3600,
exclusive=true
)3. Communicate:
mcp.send_message(
project_key=...,
sender_name="BlueLake",
to=["GreenCastle"],
subject="Review needed",
body_md="Please review the auth changes..."
)Macros for Speed
When speed matters more than fine-grained control:
mcp.macro_start_session(...) # Ensure project + register + fetch inbox
mcp.macro_prepare_thread(...) # Align with existing thread
mcp.macro_file_reservation_cycle(...) # Reserve + work + release
mcp.macro_contact_handshake(...) # Request contact permissionsDestructive Command Guard (dcg)
dcg is a high-performance Claude Code hook that blocks dangerous git and filesystem commands before they execute. Built in Rust for sub-millisecond latency, it provides mechanical enforcement of safety rules that instructions alone cannot guarantee.
Why dcg Exists
On December 17, 2025, an AI agent ran git checkout -- on files containing hours of uncommitted work from a parallel coding session. The files were recovered via git fsck --lost-found, but the incident made one thing clear: instructions in AGENTS.md don't prevent execution. dcg provides mechanical enforcement.
What Gets Blocked
| Category | Commands |
|---|---|
| Git Reset | git reset --hard, git reset --merge |
| File Discard | git checkout -- <files>, git restore <files> |
| Force Push | git push --force / -f (allows --force-with-lease) |
| Clean | git clean -f (allows -n dry-run) |
| Branch Delete | git branch -D (allows -d) |
| Stash Loss | git stash drop, git stash clear |
| Filesystem | rm -rf |
What Gets Allowed
Safe variants are allowlisted:
git checkout -b <branch>— Creates branch, doesn't touch filesgit restore --staged— Only unstages, doesn't discardgit clean -n— Dry-run preview- Temp directory cleanup still requires explicit human approval when an agent would delete files
Installation
acfs update --stack-onlyClaude Code Configuration
Add to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "dcg"}]
}
]
}
}Modular Pack System
dcg uses a modular pack system for extensibility. Enable additional packs in ~/.config/dcg/config.toml:
[packs]
enabled = [
"database.postgresql",
"containers.docker",
"kubernetes",
]Available packs: database.*, containers.*, kubernetes.*, cloud.*, infrastructure.*, system.*, package_managers.
Repo Updater (ru)
ru is a production-grade CLI tool for synchronizing collections of GitHub repositories and automating commit workflows across dirty repos with AI assistance.
Core Features
- Multi-repo sync: Clone missing repos, pull updates, detect conflicts
- Commit sweep: groups uncommitted changes across repositories into logical conventional commits (plan first,
--executeto apply) - AI code review: Orchestrate Claude Code review sessions for open issues/PRs
- Work-stealing queue: Parallel execution with load-balanced workers
- NTM integration: Session management via Named Tmux Manager
Quick Start
{ curl -fsSL "https://cdn.jsdelivr.net/gh/Dicklesworthstone/repo_updater@main/install.sh" || curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/repo_updater/main/install.sh"; } | bashInitialize configuration:
# Initialize configuration
ru init --example
# Sync all repositories
ru sync
# Check status without changes
ru statusCommit Sweep Workflow
The commit-sweep command groups the changes in your dirty worktrees into
logical conventional commits:
# Preview the commit plan (dry run is the default)
ru commit-sweep
# Execute the planned commits
ru commit-sweep --execute
# Keep manually staged files as their own group; limit to matching repos
ru commit-sweep --respect-staging --repos='*_rust' --executeTwo-Step Workflow:
- Plan: Inspects every dirty worktree and prints the commits it would make
- Execute: With
--execute, makes those commits with deterministic git commands (never pushes)
Configuration
# ~/.config/ru/config
PROJECTS_DIR=/data/projects
LAYOUT=flat # flat|owner-repo|full
UPDATE_STRATEGY=ff-only # ff-only|rebase|merge
PARALLEL=4Repo list format (~/.config/ru/repos.d/public.txt):
owner/repo
owner/repo@develop # Pin to branch
owner/repo as custom-name # Custom directory nameGet Image from Internet Link (giil)
giil downloads full-resolution images from cloud photo shares to your terminal. Essential for remote debugging workflows where you need to analyze screenshots in SSH sessions.
Supported Platforms
| Platform | Method | Speed |
|---|---|---|
| iCloud | 4-tier capture strategy | 5-15s |
| Dropbox | Direct curl download | 1-2s |
| Google Photos | Network interception | 5-15s |
| Google Drive | Multi-tier with auth detection | 5-15s |
Usage
# Basic download
giil "https://share.icloud.com/photos/02cD9okNHvVd-uuDnPCH3ZEEA"
# Output: /current/dir/icloud_20240115_143245.jpg
# Download to specific directory
giil "..." --output ~/Downloads
# Get JSON metadata
giil "..." --json
# Download all photos from album
giil "..." --all --output ~/albumInstallation
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/giil/main/install.sh?v=3.0.0" | bashVisual Debugging Workflow
- Screenshot UI bug on iPhone
- Wait for iCloud sync to Mac
- Share via Photos.app → Copy iCloud Link
- Paste link into remote terminal running Claude Code
giilfetches the image locally- AI assistant analyzes the screenshot
Chat Shared Conversation to File (csctf)
csctf converts public AI conversation share links into clean, searchable Markdown and HTML transcripts. Perfect for archiving AI conversations, building knowledge bases, and sharing with teams.
Supported Providers
| Provider | URL Pattern |
|---|---|
| ChatGPT | chatgpt.com/share/* |
| Gemini | gemini.google.com/share/* |
| Grok | grok.com/share/* |
| Claude | claude.ai/share/* |
Usage
# Basic conversion
csctf https://chatgpt.com/share/69343092-91ac-800b-996c-7552461b9b70
# Creates: <slug>.md and <slug>.html
# Markdown only
csctf "..." --md-only
# Publish to GitHub Pages
csctf "..." --publish-to-gh-pages --yes
# JSON metadata output
csctf "..." --jsonInstallation
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/chat_shared_conversation_to_file/main/install.sh | bashOutput Features
- Markdown: Clean formatting with preserved code blocks and language hints
- HTML: Zero-JavaScript static page with syntax highlighting
- Deterministic filenames:
<slug>_YYYYMMDD.mdfor reliable archival - Collision handling: Auto-increments suffix to avoid overwrites
CI/CD
ACFS does not use GitHub Actions: every workflow under .github/workflows/ is disabled, and the checks below run locally or on the maintainer's own machines (scripts/release-doctor.sh, scripts/checksum-monitor-local.sh on a systemd timer, the Docker and QEMU factory harnesses, and dsr for releases). The workflow descriptions that follow document what each disabled workflow used to do and what replaced it.
Installer Testing (installer.yml)
# Runs on every push and PR
jobs:
shellcheck:
- Lints all bash scripts with ShellCheck
integration:
- Matrix tests across Ubuntu 24.04, 25.04, 25.10
- Runs full installation in Docker
- Verifies all tools installed correctly
- Runs acfs doctor to confirm health
factory-e2e:
- Runs the literal public curl|bash installer on QEMU/KVM or a fresh real Ubuntu host
- Requires systemd, SSH, and a disposable factory VM/VPS semantics
- Verifies ubuntu user creation, SSH key merge, user services, tool health, and idempotencyDocker catches shell and package regressions early. The factory E2E is the authoritative release gate for the real beginner VPS path because it exercises systemd, SSH, login/user-service behavior, and provider image defaults that containers cannot model. A Docker pass is not sufficient release proof by itself.
Local Release Doctor (scripts/release-doctor.sh)
Run the local release gate before tagging or publishing a release candidate:
bash scripts/release-doctor.sh --full --network=check
bash scripts/release-doctor.sh --json --full --network=check > release-doctor.jsonFor a fast local readiness check while developing:
bash scripts/release-doctor.sh --jsonThe release doctor composes the maintainer checks that are easy to forget:
- branch policy and clean worktree status
shellcheck install.sh scripts/**/*.sh- manifest/generated/checksum drift via
scripts/check-manifest-drift.sh --json --quiet - verified-installer checksum candidate review with
--network=check - website
type-check,lint, and production build whenapps/webchanged or--fullis set
The checksum candidate check uses the canonical updater output. If the generated body differs from checksums.yaml, review the diff before release; if only the timestamp header differs, leave checksums.yaml unchanged. The default --network=skip keeps routine runs offline, and --web=auto runs website checks only when web files changed unless --full or --web=always is provided.
Stack Provenance Report (scripts/stack-provenance-report.sh)
Use the stack provenance report when reviewing Agent Flywheel stack tool freshness before release:
bash scripts/stack-provenance-report.sh --json
bash scripts/stack-provenance-report.sh --network=check --jsonOffline mode reports local manifest/checksum consistency for stack tools. Network mode also checks GitHub latest release metadata and generates a checksum candidate without writing checksums.yaml. Changed stack installer hashes fail the report, unrelated checksum diffs are called out separately, and rch release changes are flagged as mandatory checksum-refresh review items.
Network mode also records, per tool, which Linux architectures the latest release ships a build for (architecture.linux). A release with an x86_64 Linux build but no aarch64 one is a warning, because ARM64 installs then compile from source or fail. As of 2026-10-09, rch is the only such stack tool (upstream issue remote_compilation_helper#96).
Agent Readiness Audit (scripts/agent-readiness-audit.sh)
Run the local agent readiness audit before launching a swarm on a freshly installed VPS (the installer ships it; from a checkout, bash scripts/agent-readiness-audit.sh is equivalent):
acfs agent-readiness
acfs agent-readiness --json
acfs agent-readiness --rehearse --profile claude:work --profile codex:main # plan only
acfs agent-readiness --rehearse --profile claude:work --profile codex:main --run # run local CAAM status/--version checksRehearsal uses isolated profiles from caam profile ls, never activates, logs in or rotates accounts, and only proves local startup, not live authentication (see acfs agent-readiness --rehearse --help for the opt-in live check).
The audit checks Claude Code, Codex CLI, Antigravity CLI, and caam without printing token values or auth file contents. It reports CLI presence, version availability, parseable auth/config files, CAAM default profile consistency, and stale CAAM defaults that point at missing profiles.
Useful options:
acfs agent-readiness --no-version # Skip CLI --version probes
acfs agent-readiness --home /home/ubuntu --path "$PATH"Treat failures as launch blockers. Warnings usually mean the CLI is installed but needs a user sign-in or CAAM default profile selection.
Website Deployment (website.yml)
# Builds and deploys the Next.js wizard
jobs:
build:
- Type-check TypeScript
- Run ESLint
- Build production bundle
deploy:
- Deploy to Vercel (production)Automated Checksum + Drift Repair (scripts/checksum-monitor-local.sh)
ACFS checks upstream installer changes from a local systemd timer (every 15 minutes, in a dedicated clone on a maintainer box). The retired GitHub Actions monitor is kept for reference; it is not the publication path.
scripts/checksum-monitor-local.sh # the monitor
scripts/templates/acfs-checksum-monitor.* # timer + service templatesHow It Works:
- Bind a clean base: Requires a clean
maincheckout matching both remote compatibility refs. Installs manifest dependencies with--frozen-lockfile --ignore-scriptsand checks generated artifacts withscripts/check-manifest-drift.sh --json. Existing drift stops the run and must be repaired separately. - Observe the complete installer set: Downloads every required installer and binds its URL and SHA256 to the exact existing
checksums.yaml. Any fetch error, skipped entry, malformed report, or repository change stops publication. - Select prior authorization: For changed URLs outside the canonical
https://raw.githubusercontent.com/Dicklesworthstone/owner path, records review evidence in a GitHub issue. A human must review the upstream bytes and authorize either the complete external digest withauthorize:<digest>or an explicitly reviewed subset using the command below. Digests bind external tool names, URLs, expected pins and observed hashes; unrelated commits and trusted changes do not invalidate them. The authorization file must be owner-only, regular, single-link, and non-symlink. A missing, malformed, stale, or unsafe record grants no external authority. - Build and validate offline: Uses the complete first observation to construct the candidate, without downloading installers a second time. Selects trusted-owner mismatches and exactly the authorized external tools; every deferred external pin stays unchanged. The validator checks the exact selected mismatch set, unchanged URLs and unchanged unselected pins. Its default, without explicit selection, still requires all observed mismatches. A changing external endpoint therefore cannot invalidate trusted publication through a second fetch. If issue delivery fails, the bound evidence stays in local state with a warning; approved external changes still require recorded review before mutation.
- Generate and publish: Places the validated candidate, regenerates its installer ledger and web metadata, checks the complete generated contract, and commits only the declared publication paths. Rechecks retained evidence and selected authorization after remote probes, immediately before commit and push, and before accepting remote readback. Pushes both compatibility refs atomically and reads them back before recording the result. Pending external review reports
result=deferred state=DEFERRED, including after an approved subset publishes, and leaves the deferred review open. An accepted run with no pending review recordsHEALTHY. Authorization is consumed only after accepted external publication.
Review an external subset
Use the complete saved verification report from the monitor host and its matching checksum policy. For example, to prepare review of Atuin, oh-my-pi, and uv while leaving other external changes deferred:
bash scripts/lib/security.sh --review-checksum-subset \
checksums.yaml /path/to/verification-report.json atuin omp uvThis offline command validates the complete policy/report pair and prints a JSON
review proposal. It does not fetch or execute installers, write authorization,
or change pins. The proposal identifies selected and deferred external tools,
exact selected observations, policy/report hashes, and an authorization_line:
authorize-subset:<digest>:atuin,omp,uvThe subset digest uses the separate acfs.external-checksum-authorization.v2
schema and binds the sorted selected observations. It is not the aggregate
digest printed by an earlier four-tool issue. Selected tools must be distinct
external mismatches in the complete report; unknown, unchanged, trusted-owner,
or stale selections fail rather than being silently removed. A later change to
any selected tuple invalidates the grant. Unselected observations remain outside
its authority.
A human must first inspect the monitor's retained original bodies and headers,
compare the expected and observed versions, and establish upstream provenance.
Reproducible hashes alone do not approve content. Only after that review should
the human place the proposal's exact line, with one terminating LF and no other
bytes, in the monitor's private authorization file. Subset records are limited
to 4,096 bytes and require names in sorted order. The monitor freezes the chosen
record and its permission mode for the run; replacement, revocation, or mode
changes stop acceptance. After verified publication it replaces that record
with used-subset:<digest>:<sorted-tools>:<commit>. Legacy aggregate records
retain their existing format and checks.
The strict policy loader rejects raw . and .. URL path components. Trusted
raw-owner paths must also use literal, unencoded path spelling, so prefix
classification cannot grant automatic authority to a path with obscured owner
boundaries. Query and fragment values do not participate in this path rule.
The drift check combines bun run generate:diff with semantic checks across scripts/generated/doctor_checks.sh, apps/web/lib/generated, acfs/onboard/lessons, README snippets, and installer checksum coverage.
The internal ledger is inert data: exactly ACFS_INTERNAL_CHECKSUMS_SCHEMA=1,
one associative checksum map, and its exact entry count. The installer parses
that closed grammar without sourcing the ledger, enforces its checksum-controlled
membership, and verifies regular non-symlink files before sourcing them. This
is an internal consistency boundary, not an independent signature or archive
provenance claim.
First-party changes can publish automatically after these checks. External changes require review before publication, rather than a post-publication alert. Repeated failures are logged and escalated through deduplicated GitHub issues and optional ACFS_NTFY_TOPIC notifications. Check the deployed timer's logs for its current health; the existence of workflow or timer files does not prove that monitoring is running.
Alerts begin after three consecutive failures and normally repeat after 24 hours for an unchanged reason, with separate cooldowns for GitHub and push delivery. Saved numbers are read as decimal; oversized failure counts saturate at 2,147,483,647. Invalid or future cooldown timestamps cannot silence alerts. An unavailable clock does not advance a delivery record.
Production Smoke Tests (production-smoke.yml)
Validates deployments on real environments:
# Runs after deployment
jobs:
smoke:
- Fetches install.sh from production URL
- Verifies checksum matches repository
- Validates shell syntax
- Confirms no uncommitted driftInstaller Canary (Docker) (installer-canary.yml)
Runs the installer inside fresh Ubuntu containers on a daily schedule. This is a fast regression canary, not the final proof of the factory VPS path.
schedule: "30 7 * * *" # daily
jobs:
canary:
- Run tests/vm/test_install_ubuntu.sh (vibe mode)
- Defaults to Ubuntu 24.04; --all covers the supported LTS releases 22.04, 24.04, and 26.04
- Uses ACFS_CHECKSUMS_REF=main for freshest hashesFactory Installer E2E (installer-factory-e2e.yml)
Runs the literal public installer through the authoritative factory harness. The QEMU/KVM backend uses the official Ubuntu cloud image and requires a runner with /dev/kvm; set the repository variable ACFS_FACTORY_RUNNER, the manual runner input, or client_payload.runner to a KVM-capable larger/self-hosted runner. The real-host backend runs against a disposable Ubuntu VPS over SSH and is intended for provider-specific sentinel runs.
schedule: "0 8 * * 0" # weekly QEMU/KVM factory canary when ACFS_FACTORY_RUNNER has /dev/kvm
workflow_dispatch:
inputs:
backend: qemu|real-host
runner: "" # optional override; blank uses ACFS_FACTORY_RUNNER or ubuntu-latest
ref: main
mode: vibe
expect_ubuntu: "25.10"
expect_final_ubuntu: "25.10"
repository_dispatch:
types: [acfs-factory-host-ready]
real-host secrets:
ACFS_FACTORY_SSH_PRIVATE_KEY: private key for real-host backend
ACFS_FACTORY_SSH_TARGET: optional fallback root@fresh-host for real-host backendStandard GitHub-hosted runners do not provide a contractual nested-virtualization environment. If the QEMU backend runs without /dev/kvm, the workflow fails at the KVM preflight with an environment-specific error before invoking the installer.
Reusable workflow callers may use the QEMU backend without passing SSH secrets. The real-host backend still needs a private key plus either client_payload.ssh_target for dispatch runs or ACFS_FACTORY_SSH_TARGET as a fallback.
If backend=real-host is requested without those SSH credentials, the workflow fails during configuration resolution. It must never report a green canary when no disposable host was tested.
Workflow artifact directories and uploads include only the current GitHub run id and attempt. That keeps repeated scheduled/manual runs from reusing or uploading old QEMU overlay disks on KVM-capable self-hosted runners with persistent workspaces. The QEMU backend writes its generated private SSH key outside the repository checkout, so upload-artifact and future Git commits never package guest login credentials. Factory diagnostics are redacted before local upload, including installer logs and the remote diagnostic archive.
The target host must be freshly provisioned. By default the harness fails if the ubuntu user already exists before install, because the real beginner path must prove ACFS creates that user automatically. The harness also requires acfs doctor --json to report zero failures and zero warnings, then separately verifies Agent Mail liveness/systemd service state and the ACFS nightly user timer.
For current release qualification, provision a fresh Ubuntu 24.04 LTS host and pass --expect-ubuntu 24.04 --expect-final-ubuntu 24.04 to the factory script. This verifies that an ordinary install preserves the host release. The historical workflow inputs above describe the disabled workflow's 25.10 defaults.
The separate upgrade/resume gate must explicitly request the upgrade on a disposable reboot-capable host. The factory harness's --expect-final-ubuntu and --allow-install-reboot options only control verification and reboot tolerance; --target-ubuntu 26.04 is what passes --target-ubuntu=26.04 to the installer, on both the first run and the idempotency rerun. For example, tests/vm/test_factory_install_qemu.sh --ubuntu 24.04 --target-ubuntu 26.04 --allow-install-reboot (the final-release expectation defaults to the target). On 2026-10-09 this passed for 24.04, 22.04 (two hops) and 25.10 (EOL recovery) sources, and for a fresh 26.04 image.
The disabled workflow previously accepted provider-specific real VPS sentinels through an acfs-factory-host-ready dispatch from an external provisioning job. This historical payload included the fresh host address instead of storing a long-lived VPS as ACFS_FACTORY_SSH_TARGET; current release checks run the factory script directly:
{
"event_type": "acfs-factory-host-ready",
"client_payload": {
"backend": "real-host",
"ssh_target": "root@203.0.113.10",
"ref": "main",
"mode": "vibe",
"expect_ubuntu": "25.10",
"expect_final_ubuntu": "25.10"
}
}Local QEMU Factory E2E (test_factory_install_qemu.sh)
Runs the same factory-host harness inside a real local VM instead of a Docker container. The wrapper downloads and verifies the official Ubuntu cloud image, boots it with QEMU/KVM and cloud-init, exposes root SSH on a local forwarded port, then delegates to tests/vm/test_factory_install_ubuntu.sh. Generated private SSH keys are kept outside the repository checkout; use --key-dir with a path under /tmp or another non-repo directory when you need a specific local key location for debugging.
sudo apt-get install -y qemu-system-x86 qemu-utils cloud-image-utils openssh-client
./tests/vm/test_factory_install_qemu.sh --ubuntu 24.04 --expect-final-ubuntu 24.04Use this when Docker passes but you need local proof for systemd, sshd, cloud-init, kernel, filesystem, and login behavior before spending time on a disposable provider VPS.
Playwright E2E Tests (playwright.yml)
Full browser testing of the wizard website:
# Runs on PR to main
browsers:
- Chromium
- Firefox
- WebKit
- Mobile Chrome
- Mobile Safari
tests:
- Wizard flow completion
- Step navigation
- Copy button functionality
- Responsive designVPS Providers
ACFS works on any Ubuntu VPS with SSH access and either root password login or a provider console that lets you become root for the first install. Here are recommended providers optimized for multi-agent workloads.
Why 48-64GB RAM? Each AI coding agent uses ~2GB RAM. To run 10-20+ agents simultaneously, you need 48GB+ RAM. Don't bottleneck a $400+/month AI investment to save $20 on hosting.
After installation, run acfs capacity --profile 25-agents --recommend-ntm on the VPS for a local RAM/CPU/disk sizing report with recommended agent counts and copyable NTM launch profiles.
Plan names, specs, and prices below were verified against the provider sites in 2026-08 (PRICING_LAST_UPDATED in apps/web/lib/vpsProviders.ts, the single source the wizard renders from).
Contabo (Best Value — Top Pick)
| Plan | RAM | vCPU | Storage | Price | Notes |
|---|---|---|---|---|---|
| Cloud VPS 16 | 64GB | 16 | 500GB SSD | ~$43/mo (€37 EU list) | Recommended — Best for serious multi-agent work |
| Cloud VPS 12 | 48GB | 12 | 400GB SSD | ~$29/mo (€25 EU list) | Budget option, still comfortable |
- Best specs-to-price ratio on the market
- Contabo lists EUR prices (24-month introductory rate, incl. VAT); the USD figures are approximate conversions
- Month-to-month terms are available; US datacenters may add a location fee (checkout shows the final price)
OVH (Small Plans Only)
| Plan | RAM | vCore | Storage | Price | Notes |
|---|---|---|---|---|---|
| VPS-4 | 24GB | 8 | 200GB NVMe | from $23.37/mo (12-month term) | Largest OVH VPS — below the 48GB recommendation; fine for ~4-6 standard agents |
| VPS-3 | 12GB | 6 | 100GB NVMe | from $12.32/mo (12-month term) | Far below the recommendation; only for trying ACFS with 1-2 agents |
- OVH's VPS range now tops out at VPS-4 (24GB); pick Contabo for the 48-64GB hosts this guide recommends
- Anti-DDoS included; polished control panel and fast activation
- "From" prices assume a 12-month term; month-to-month costs more
Requirements
| Requirement | Minimum | Recommended | | OS | Ubuntu 22.04+ or Arch-family (Arch, Omarchy) | Ubuntu 24.04 LTS; supported LTS hosts stay on their current release by default | | RAM | 32GB (tight) | 48-64GB | | Storage | 250GB NVMe SSD | 300GB+ NVMe SSD | | CPU | 12 vCPU | 16 vCPU | | Price | ~$29/mo (48GB) | ~$43/mo (64GB) |
Other Providers
Any provider with an Ubuntu VPS, SSH access, and a first-login root password or root console works. The wizard at agent-flywheel.com has step-by-step guides.
Provider Setup Guides
ACFS includes detailed step-by-step guides for each supported provider in scripts/providers/:
| Provider | Guide | Key Sections |
|---|---|---|
| Contabo | contabo.md | Account creation, plan selection, data center choice, root password setup |
| OVH | ovh.md | Control panel navigation, password authentication, instance configuration, networking |
| Hetzner | hetzner.md | Server sizing, SSH-key login, cloud-init template, firewall rules |
Each guide includes:
- Text-only steps (no screenshots: provider consoles change too often for them to stay accurate)
- Pricing or sizing guidance (Contabo and OVH figures mirror the wizard's provider table)
- Region selection guidance (latency, privacy)
- First-login guidance for that provider (password-first or SSH key) and post-install SSH key recovery
- Troubleshooting for common provisioning issues
Provider Comparison:
| Aspect | Contabo | OVH | Hetzner |
|---|---|---|---|
| Best For | Maximum value | EU data residency | German engineering |
| Provisioning | Minutes to ~1 hour | 5-30 minutes | 2-10 minutes |
| Support | Email only | Phone + chat | 24/7 ticket system |
| Data Centers | EU, US, Asia | Global | Germany, Finland, US, Singapore |
| Payment | Monthly | Hourly or monthly | Hourly or monthly |
Recommendation Flow:
- Budget: Contabo (best specs per dollar)
- Speed: Hetzner (instant provisioning)
- Support: OVH (phone support available)
- Privacy: Any EU provider (GDPR compliance)
Project Structure
agentic_coding_flywheel_setup/
├── README.md # This file
├── AGENTS.md # Development guidelines
├── VERSION # Current version (0.7.0)
├── install.sh # Main installer entry point
├── acfs.manifest.yaml # Canonical tool manifest
├── checksums.yaml # SHA256 hashes for upstream scripts
├── package.json # Root monorepo config
│
├── apps/
│ └── web/ # Next.js 16 wizard website
│ ├── app/ # App Router pages
│ │ ├── layout.tsx # Root layout
│ │ ├── page.tsx # Landing page
│ │ └── wizard/ # Wizard step pages
│ ├── components/ # UI components
│ └── lib/ # Utilities
│
├── packages/
│ ├── manifest/ # Manifest parser + generator
│ │ └── src/
│ │ ├── parser.ts # YAML parsing
│ │ ├── schema.ts # Zod validation schemas
│ │ ├── types.ts # TypeScript types
│ │ ├── utils.ts # Helper functions
│ │ └── generate.ts # Script generator
│ └── onboard/ # Onboard TUI source
│
├── acfs/ # Files deployed to ~/.acfs/
│ ├── zsh/
│ │ └── acfs.zshrc # Shell configuration
│ ├── tmux/
│ │ └── tmux.conf # Tmux configuration
│ └── onboard/
│ ├── onboard.sh # Onboarding TUI script
│ └── lessons/ # Tutorial markdown
│
├── scripts/
│ ├── lib/ # Installer bash libraries
│ │ ├── logging.sh # Console output
│ │ ├── security.sh # HTTPS + checksum verification
│ │ ├── os_detect.sh # OS detection
│ │ ├── user.sh # User management
│ │ ├── update.sh # Update command logic
│ │ ├── gum_ui.sh # Enhanced UI
│ │ ├── stack.sh # Flywheel stack helpers
│ │ └── doctor.sh # Health checks
│ ├── generated/ # Auto-generated from manifest
│ │ ├── install_<category>.sh # 13 source-only category libraries
│ │ ├── install_all.sh # Sourceable generated-module harness
│ │ ├── manifest_index.sh # Runtime module metadata
│ │ ├── doctor_checks.sh # Verification checks
│ │ └── internal_checksums.sh # Schema-1 critical-script checksum data
│ ├── providers/ # VPS provider guides
│ │ ├── ovh.md
│ │ ├── contabo.md
│ │ └── hetzner.md
│ └── sync/
│ └── sync_ntm_palette.sh # Sync NTM command palette
│
├── .github/
│ └── workflows/
│ ├── installer.yml # ShellCheck + Ubuntu matrix tests
│ └── website.yml # Next.js build + deploy
│
└── tests/
└── vm/
├── test_install_ubuntu.sh # Docker integration test
├── test_factory_install_ubuntu.sh # Real VM/VPS factory install test
└── test_factory_install_qemu.sh # Local QEMU/KVM factory install testDevelopment
Website Development
cd apps/web
bun install # Install dependencies
bun run dev # Dev server at http://localhost:3000
bun run build # Production build
bun run lint # Lint check
bun run type-check # TypeScript checkManifest Development
cd packages/manifest
bun install # Install dependencies
bun run generate # Generate installer scripts
bun run generate:dry # Preview without writing filesInstaller Testing
# Local lint
shellcheck install.sh scripts/lib/*.sh
# Full installer integration test on supported Ubuntu 24.04 LTS
./tests/vm/test_install_ubuntu.sh --ubuntu 24.04
# Authoritative factory-host E2E (disposable fresh Ubuntu 24.04 LTS VM/VPS)
./tests/vm/test_factory_install_ubuntu.sh --ssh-target root@203.0.113.10 --expect-ubuntu 24.04 --expect-final-ubuntu 24.04
# Local authoritative VM E2E (QEMU/KVM + official Ubuntu cloud image)
./tests/vm/test_factory_install_qemu.sh --ubuntu 24.04 --expect-final-ubuntu 24.04
# Explicit upgrade entrypoint (run as root on a disposable Ubuntu 24.04 host)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh" | bash -s -- --yes --mode vibe --target-ubuntu=26.04The explicit upgrade command starts the upgrade; it is not a complete factory E2E verdict. Capture resume, final OS, and tool-health evidence separately until the factory harness can forward the opt-in target.
Security Verification
# Print all upstream URLs
./scripts/lib/security.sh --print
# Verify all checksums
./scripts/lib/security.sh --verify
# Update checksums after reviewing upstream changes (candidate file first;
# never redirect onto checksums.yaml directly)
./scripts/lib/security.sh --update-checksums > /tmp/acfs-checksums.candidate.yaml
diff -u checksums.yaml /tmp/acfs-checksums.candidate.yaml || [[ $? -eq 1 ]]
# After reviewing and accepting the diff:
cp /tmp/acfs-checksums.candidate.yaml checksums.yamlManifest Validation
The manifest parser includes comprehensive validation beyond basic schema checking:
Validation Error Codes:
| Code | Description |
|---|---|
MISSING_DEPENDENCY | Module references non-existent dependency |
DEPENDENCY_CYCLE | Circular dependency detected (A→B→C→A) |
PHASE_VIOLATION | Module runs before its dependencies |
FUNCTION_NAME_COLLISION | Two modules generate same bash function |
INVALID_VERIFIED_INSTALLER_RUNNER | Runner not in allowlist (bash/sh only) |
Running Validation:
cd packages/manifest
bun run validate # Full validation
bun run validate --verbose # Show all checksCycle Detection Algorithm:
Tarjan's strongly connected components (SCC):
1. DFS with discovery/low-link tracking
2. Identify SCCs with size > 1 as cycles
3. Report cycle path for human debuggingTest Harness
ACFS includes a comprehensive test harness (tests/vm/lib/test_harness.sh) for integration testing:
# Source the harness
source tests/vm/lib/test_harness.sh
# Initialize test suite
harness_init "ACFS Installation Tests"
# Create test sections
harness_section "Phase 1: Base Packages"
# Run commands with automatic logging
harness_run "Installing curl" apt install -y curl
# Assert results
harness_pass "curl installed successfully"
harness_fail "curl installation failed"
harness_skip "Skipping optional test"
# Generate summary
harness_summary # Outputs: 15 passed, 0 failed, 2 skippedTest Files:
| Test | Purpose |
|---|---|
test_install_ubuntu.sh | Full Docker-based installation |
test_factory_install_ubuntu.sh | Real systemd VM/VPS factory install from public curl|bash |
test_factory_install_qemu.sh | Local QEMU/KVM factory install using Ubuntu cloud images |
test_acfs_update.sh | Update mechanism validation |
bootstrap_offline_checks.sh | Offline system readiness |
resume_checks.sh | State resume validation |
selection_checks.sh | Module selection unit tests |
selection_e2e.sh | End-to-end selection flow |
Running Tests:
# Full Docker integration test on supported Ubuntu 24.04 LTS
./tests/vm/test_install_ubuntu.sh --ubuntu 24.04
# Docker integration matrix across the supported LTS releases (22.04, 24.04, 26.04)
./tests/vm/test_install_ubuntu.sh --all
# Interrupted install (SIGHUP after cli_tools) that --resume must finish
./tests/vm/test_install_ubuntu.sh --interrupt-resume
# arm64 container install (needs binfmt emulation on x86_64 hosts)
./tests/vm/test_install_ubuntu.sh --platform linux/arm64
# Real factory-host integration test preserving Ubuntu 24.04 LTS
./tests/vm/test_factory_install_ubuntu.sh --ssh-target root@203.0.113.10 --expect-ubuntu 24.04 --expect-final-ubuntu 24.04
# Local QEMU/KVM factory-host integration test
./tests/vm/test_factory_install_qemu.sh --ubuntu 24.04 --expect-final-ubuntu 24.04
# Explicit upgrade entrypoint (root on a disposable Ubuntu 24.04 host;
# collect resume and final-state evidence separately)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh" | bash -s -- --yes --mode vibe --target-ubuntu=26.04
# Selection logic tests
./tests/vm/selection_checks.sh
# Web E2E tests
./tests/web/run_e2e.shSync Scripts
Sync scripts keep ACFS documentation aligned with upstream projects:
# Sync NTM command palette from upstream
./scripts/sync/sync_ntm_palette.sh
# Check if update available (without downloading)
./scripts/sync/sync_ntm_palette.sh --checkCurrent Sync Sources:
| Script | Source | Destination |
|---|---|---|
sync_ntm_palette.sh | NTM repo command_palette.md | acfs/onboard/docs/ntm/ |
All sync scripts use the security library for HTTPS enforcement and content hashing.
Website Design System
The website uses a comprehensive design system (apps/web/lib/design-tokens.ts):
Color Tokens (OKLCH Color Space):
// Perceptually uniform colors
colors: {
cyan: "oklch(0.75 0.18 195)", // Primary accent
pink: "oklch(0.7 0.2 330)", // Secondary accent
purple: "oklch(0.65 0.18 290)", // Tertiary
success: "oklch(0.72 0.19 145)", // Green
warning: "oklch(0.78 0.16 75)", // Yellow
error: "oklch(0.65 0.22 25)", // Red
}Shadow Tokens:
shadows: {
cardHover: "0 20px 40px -12px oklch(0.75 0.18 195 / 0.15)",
cardLifted: "0 25px 50px -12px oklch(0.75 0.18 195 / 0.2)",
primaryGlow: "0 0 40px -8px oklch(0.75 0.18 195 / 0.3)",
}Animation Presets:
animations: {
hover: { scale: 1.02, transition: { duration: 0.2 } },
tap: { scale: 0.98 },
fadeIn: { opacity: [0, 1], transition: { duration: 0.3 } },
}Accessibility:
- Reduced motion support via
useReducedMotionhook - Semantic HTML structure
- ARIA labels on interactive elements
- Keyboard navigation support
Requirements
- Runtime: Bun (not npm/yarn/pnpm)
- Node: Latest
- Shell: Bash 5+
FAQ
Why "Vibe Mode"?
Vibe mode is designed for throwaway VPS environments where velocity matters more than safety:
- Passwordless sudo eliminates friction
- Agent dangerous flags skip confirmation dialogs
- Pre-configured aliases for maximum speed
Never use vibe mode on production or shared systems.
Can I use this on my local machine?
ACFS is designed for fresh Ubuntu VPS instances. While you could run it locally:
- It may conflict with existing configurations
- It assumes root/sudo access
- It's not designed for macOS or Windows
For local development, use the individual tools directly.
What if the installer fails?
The installer is checkpointed. Simply re-run it:
{ acfs_installer="$(curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh")" || acfs_installer="$(curl -fsSL "https://cdn.jsdelivr.net/gh/Dicklesworthstone/agentic_coding_flywheel_setup@main/install.sh")"; } && printf '%s\n' "$acfs_installer" | bash -s -- --yes --mode vibeIt revisits installation phases even when an older checkpoint marked them complete. Component checks skip working installations while missing dependencies and interrupted service setup can be repaired. Mandatory step failures remain in partial_phases and prevent phase completion; explicitly optional failures remain non-fatal. Final success requires the essential CLI tools and, unless explicitly skipped, a working local PostgreSQL query as the target user.
How do I update tools?
Use the built-in update command:
acfs update # Update all standard components
acfs update --stack # Include Agent Flywheel stack
acfs update --agents-only # Just update AI agentsHow do I uninstall?
There's no uninstall script. To reset:
- Delete the VPS instance
- Create a new one
- Run the installer fresh
This is intentional—ACFS is designed for ephemeral VPS environments.
Can I customize which tools are installed?
Currently, ACFS installs the full suite. Future versions will support:
- Manifest-based tool selection
- Interactive mode for choosing components
- Modular installation scripts
Why ACFS Exists
The Problem: The Agentic Coding Barrier
The rise of AI coding agents (Claude Code, Codex CLI, Antigravity CLI) has created a new paradigm in software development. These agents can write code, debug issues, and even architect solutions—but only if they have the right environment.
The barrier isn't the agents themselves. It's the hours of setup required to create an environment where agents can actually be productive:
┌────────────────────────────────────────────────────────────────────────────┐
│ TIME INVESTMENT WITHOUT ACFS │
│ │
│ VPS Setup ..................... 30-60 min │
│ Shell Configuration ........... 20-30 min │
│ Language Runtimes ............. 30-45 min │
│ Dev Tools ..................... 20-30 min │
│ Agent Installation ............ 15-30 min │
│ Agent Configuration ........... 20-40 min │
│ Coordination Tools ............ 30-60 min │
│ Troubleshooting ............... 30-120 min │
│ ───────────────────────────────────────── │
│ TOTAL: 3-7 hours (and that's if everything works) │
│ │
│ TIME INVESTMENT WITH ACFS │
│ │
│ Run one command ............... 25-30 min │
│ ───────────────────────────────────────── │
│ TOTAL: 30 minutes │
└────────────────────────────────────────────────────────────────────────────┘ACFS eliminates this barrier entirely. One command, 30 minutes, fully configured.
The Deeper Problem: Beginners Can't Start
For experienced developers, the setup is tedious but doable. For beginners—the people who would benefit most from AI coding assistance—it's an insurmountable wall:
- What's SSH? How do I generate keys?
- What's a VPS? How do I rent one?
- What's a terminal? Which one should I use?
- How do I connect to a remote server?
- What are all these tools and why do I need them?
The wizard website at agent-flywheel.com solves this by providing:
- Absolute beginner guidance — Explains every concept in plain English
- OS-specific instructions — Detects Mac vs Windows, shows the right commands
- Visual confirmations — Checkboxes for each step, copy buttons for commands
- Troubleshooting help — Expandable sections for common problems
- Progress persistence — Resume where you left off across browser sessions
The 10x Multiplier Effect
ACFS isn't just a collection of tools—it's a carefully curated system where each component amplifies the others. The value isn't additive; it's multiplicative.
Tool Synergy Model
┌─────────────────┐
│ PRODUCTIVITY │
│ MULTIPLIER │
└────────┬────────┘
│
┌─────────────────────────────┼─────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ ENVIRONMENT │ │ AGENTS │ │ COORDINATION │
│ LAYER │ │ LAYER │ │ LAYER │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ • zsh + p10k │────────▶│ • Claude Code │────────▶│ • Agent Mail │
│ • tmux │ │ • Codex CLI │ │ • NTM │
│ • Modern CLI │ │ • Antigravity │ │ • SLB + DCG │
│ • Language VMs │ │ │ │ • Beads Viewer │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ Each layer enables │ Agents become more │
│ the next layer │ powerful together │
└─────────────────────────────┴─────────────────────────────┘Why These Specific Tools?
Every tool in ACFS earns its place through concrete productivity gains:
| Tool | Individual Value | Synergy Value |
|---|---|---|
| tmux | Persistent sessions | Agents can work while you're disconnected |
| NTM | Organized sessions | One command spawns 10 agents in named windows |
| Agent Mail | Message passing | Agents coordinate without conflicts |
| SLB | Two-person rule | Dangerous operations require confirmation |
| DCG | Command guardrails | Blocks destructive commands before execution |
| Beads Viewer | Task tracking | Agents can see project state, avoid rework |
| atuin | Shell history | Search commands across sessions, share patterns |
| zoxide | Smart cd | z proj beats cd ~/projects/my-long-name |
| ripgrep | Fast search | Agents find code 100x faster than grep |
| fzf | Fuzzy finding | Interactive selection instead of typing paths |
The Compounding Effect
A single agent with basic tooling is useful. Three agents with:
- A shared project structure
- Coordination via Agent Mail
- Orchestration via NTM
- Safety guardrails via SLB
- DCG guard hook (blocks destructive commands before execution)
- Task visibility via Beads
...can accomplish in one day what would take a solo developer a week.
Tip: run acfs services-setup to configure logins, and enable DCG for destructive-command protection.
This is the flywheel effect in action. Better tools → more capable agents → more code shipped → better understanding of what tools are needed → better tools.
Design Algorithms & Decisions
ACFS implements several algorithmic patterns that ensure reliability and maintainability.
Idempotency Algorithm
Every installation function follows the check-before-install pattern:
install_tool() {
if command_exists "tool"; then
log_success "tool already installed"
return 0
fi
# ... installation logic ...
if command_exists "tool"; then
log_success "tool installed successfully"
return 0
else
log_error "tool installation failed"
return 1
fi
}This guarantees:
- Safe re-runs — Running the installer twice doesn't break anything
- Resume capability — Failures don't require starting over
- Declarative intent — The end state is defined, not the transition
Checksum Verification Algorithm
The security system uses content-addressable verification:
┌─────────────────────────────────────────────────────────────────────────┐
│ VERIFICATION FLOW │
│ │
│ 1. Download script to memory (not disk) │
│ 2. Calculate SHA256 of downloaded content │
│ 3. Compare against stored hash in checksums.yaml │
│ 4. If match → execute │
│ 5. If mismatch → refuse execution, report discrepancy │
│ │
│ Key insight: We verify CONTENT, not just transport │
│ (HTTPS only protects the channel, not the content at source) │
└─────────────────────────────────────────────────────────────────────────┘Manifest-Driven Generation
The generator uses a template expansion pattern:
- Parse — Read YAML manifest, validate with Zod schemas
- Transform — Convert manifest entries to installation functions
- Group — Organize by category (base, shell, cli, lang, agents, etc.)
- Generate — Emit Bash scripts with consistent structure
- Verify — Generate doctor checks from verification commands
The manifest is the source of truth for generated modules and their verification
metadata. Explicitly authored orchestration handoffs remain in install.sh and
the installer libraries, so generator drift checks and integration tests are
still required across that boundary.
Code Generator Architecture
The manifest generator (packages/manifest/src/generate.ts) is a sophisticated TypeScript program that transforms YAML into bash:
Input Processing:
// 1. Parse YAML with validation
const manifest = parseManifestFile(MANIFEST_PATH); // Zod-validated
// 2. Load checksums for verified installers
const checksums = parseYaml(readFileSync(CHECKSUMS_PATH));
// 3. Topological sort for dependency order
const sorted = sortModulesByInstallOrder(manifest.modules);Security-First Code Generation:
// Shell-safe quoting (prevents command injection)
function shellQuote(s: string): string {
return `'${s.replace(/'/g, "'\\''")}'`;
}
// Allowlisted runners only (belt-and-suspenders)
const ALLOWED_RUNNERS = ['bash', 'sh'] as const;
// Verified installer file-backed execution
function buildVerifiedInstallerFileCommand(module: Module): string {
// Runs the interpreter only after the complete script is checksum-verified
// and staged in a read-only file.
}Output Structure:
scripts/generated/
├── install_base.sh # Base system packages (apt)
├── install_users.sh # Source-only library; users.ubuntu is orchestration-owned
├── install_filesystem.sh # Directory structure (/data/projects)
├── install_shell.sh # zsh + oh-my-zsh + p10k
├── install_cli.sh # ripgrep, tmux, fzf, lazygit, etc.
├── install_network.sh # Tailscale
├── install_lang.sh # bun, uv, rust, go
├── install_tools.sh # ast-grep, atuin, zoxide, Vault
├── install_agents.sh # claude, codex, agy
├── install_db.sh # PostgreSQL 18
├── install_cloud.sh # wrangler, supabase, vercel
├── install_stack.sh # Flywheel stack + utilities
├── install_acfs.sh # ACFS config deployment
├── install_all.sh # Sourceable generated-module harness
├── doctor_checks.sh # Health verification
├── manifest_index.sh # Module metadata arrays
└── internal_checksums.sh # Schema-1 critical-script checksum dataGenerated Script Structure:
#!/bin/bash -p
# AUTO-GENERATED FROM acfs.manifest.yaml - DO NOT EDIT
acfs_generated_install_module_id() {
local module_id="module.id"
acfs_require_contract "module:${module_id}" # Validate environment
acfs_generated_ensure_selection
should_run_module "$module_id" || return 0
run_as_target_shell <<'HEREDOC'
# Installation commands from manifest
HEREDOC
}Installed-check/idempotency and aggregate-failure behavior are owned by the
install.sh phase dispatcher. Generated files refuse direct execution; use
install.sh for a complete run.
Regeneration:
cd packages/manifest
bun run generate # Full regeneration
bun run generate:dry # Preview without writingRegeneration checks every destination and the marked README regions before writing. Unsafe output entries, including dangling links, or broken region markers cause a preflight rejection that preserves existing outputs. Each file is replaced atomically; the complete output set is not a transaction against concurrent edits or later I/O failures.
Generated Manifest Index
The generator produces manifest_index.sh, a comprehensive bash metadata file that provides programmatic access to manifest data at runtime:
Associative Arrays:
# Module metadata lookup
declare -gA ACFS_MODULE_DESC
ACFS_MODULE_DESC["lang.bun"]="Bun JavaScript/TypeScript runtime"
ACFS_MODULE_DESC["agents.claude"]="Claude Code CLI agent"
# Phase mapping (determines install order)
declare -gA ACFS_MODULE_PHASE
ACFS_MODULE_PHASE["base.system"]="1"
ACFS_MODULE_PHASE["lang.bun"]="6"
ACFS_MODULE_PHASE["agents.claude"]="7"
# Dependency relationships (comma-separated)
declare -gA ACFS_MODULE_DEPS
ACFS_MODULE_DEPS["agents.codex"]="lang.bun"
ACFS_MODULE_DEPS["stack.mcp_agent_mail"]="lang.bun,lang.uv"
# Generated function name mapping
declare -gA ACFS_MODULE_FUNC
ACFS_MODULE_FUNC["lang.bun"]="acfs_generated_install_lang_bun"
# generated:false modules deliberately have no ACFS_MODULE_FUNC entry
# Canonical category order and per-module generation ownership
declare -a ACFS_CATEGORIES_IN_ORDER=(base users filesystem shell cli network lang tools db cloud agents stack acfs)
declare -gA ACFS_MODULE_GENERATED
ACFS_MODULE_GENERATED["lang.bun"]="1"
ACFS_MODULE_GENERATED["users.ubuntu"]="0"
# Category grouping
declare -gA ACFS_MODULE_CATEGORY
ACFS_MODULE_CATEGORY["lang.bun"]="lang"
# Default inclusion in install
declare -gA ACFS_MODULE_DEFAULT
ACFS_MODULE_DEFAULT["lang.bun"]="1"
ACFS_MODULE_DEFAULT["db.postgres18"]="1"Runtime Access Pattern:
# Iterate modules in deterministic install order
for module in "${ACFS_MODULES_IN_ORDER[@]}"; do
[[ "${ACFS_MODULE_CATEGORY[$module]}" == "agents" ]] || continue
printf '%s\n' "$module"
done
# Check if module is default-installed
[[ "${ACFS_MODULE_DEFAULT[tools.vault]:-1}" == "1" ]]
# Get installation phase
printf '%s\n' "${ACFS_MODULE_PHASE[stack.ntm]}" # 9Use Cases:
acfs doctorqueries module metadata for health checksinstall.sh --list-modulesdisplays available modules--skip <module>validates module existence before skipping--only-phase <n|name>uses phase mapping for selective installs
The manifest index bridges the TypeScript generator with bash runtime, enabling sophisticated module selection logic while keeping the bash scripts simple.
Progressive Disclosure in the Wizard
The wizard website implements progressive disclosure for complexity management:
Level 1: Core instructions (visible by default)
├── Copy this command
├── Paste in terminal
└── Press Enter
Level 2: Troubleshooting (expandable)
├── "Permission denied" → fix instructions
├── "Command not found" → prerequisites
└── "Connection refused" → diagnostics
Level 3: Deep explanations (collapsible "Beginner Guide")
├── What is SSH?
├── What is a VPS?
├── Why these specific steps?
└── What happens under the hood?This allows beginners to get deep context when needed, while experts can skip straight to the commands.
Multi-Agent Orchestration Model
ACFS is designed for multi-agent workflows where several AI coding agents work on the same project simultaneously.
The Coordination Problem
Without coordination, multiple agents cause chaos:
- File conflicts — Two agents edit the same file
- Duplicated work — Agents solve the same problem independently
- Communication gaps — No visibility into what others are doing
- Safety risks — Dangerous operations without oversight
The ACFS Solution Stack
┌───────────────────────────────────────────────────────────────────────────┐
│ AGENT COORDINATION LAYER │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Agent Mail │ │ NTM │ │ SLB + DCG │ │ Beads │ │
│ │ (Messaging) │ │ (Sessions) │ │ (Safety) │ │ (Tasks) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ │ ┌────────────┴────────────────┴────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ FILE RESERVATION SYSTEM │ │
│ │ │ │
│ │ Agent A reserves: src/auth/** │ │
│ │ Agent B reserves: src/api/** │ │
│ │ Agent C reserves: tests/** │ │
│ │ │ │
│ │ → No conflicts, parallel progress │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────────┘Agent Communication Patterns
1. Direct Messaging (Agent Mail)
Agent A → Agent B: "I finished the auth module, ready for API integration"
Agent B → Agent A: "ACK, starting API integration with auth dependency"2. Broadcast Updates (Thread Summaries)
Thread: "Sprint 23 Tasks"
├── Agent A: "Claimed user-registration feature"
├── Agent B: "Claimed api-endpoints feature"
├── Agent C: "Claimed test-coverage task"
└── All agents see project state3. File Reservations (Conflict Prevention)
Agent A: reserve_paths(["src/auth/*"], exclusive=true, ttl=3600)
Agent B: reserve_paths(["src/auth/*"]) → CONFLICT: held by Agent A
Agent B: reserve_paths(["src/api/*"]) → GRANTEDThe NTM Orchestration Pattern
Named Tmux Manager (NTM) enables the one-command swarm spawn:
# Spawn 10 agents, each in a named tmux window
ntm spawn \
--count 10 \
--prefix "agent-" \
--command "claude --dangerously-skip-permissions"Result:
tmux session: acfs-swarm
├── agent-1: Claude working on auth
├── agent-2: Claude working on api
├── agent-3: Claude working on tests
├── agent-4: Codex reviewing PRs
├── agent-5: Antigravity writing docs
└── ...Capacity and Resource Profiles
acfs capacity --profile 25-agents --recommend-ntm reports local CPU, memory,
and disk capacity. Optional resource-profile wrappers provide scheduling
weights and accounting; explicit memory limits can contain a runaway command
and its children in a systemd user scope.
Preview a profile, then repeat the reviewed values to apply it:
acfs capacity --resource-profile \
--memory-max-mib 8192 --memory-swap-max-mib 2048
acfs capacity --resource-profile \
--memory-max-mib 8192 --memory-swap-max-mib 2048 \
--apply-resource-profile
source ~/.acfs/resource-profile/acfs-resource-profile.sh
ccs # Claude; cods and gmis wrap Codex and GeminiThese example limits are per scope: 8 GiB of memory and a separate 2 GiB of
swap. The swap limit is optional and requires a memory limit. Each accepts
integer MiB up to 1048576; memory must be positive, while swap may be zero.
The limits apply to all explicit acfs-scope classes. A bounded wrapper checks
the actual cgroup v2 limits before starting the command and refuses to run it
uncontained if the tools, user manager, or limit evidence are unavailable.
Without memory options, the profile keeps its existing weights/accounting and
direct-execution fallback. Ordinary aliases and NTM templates are unchanged.
Multiple bounded scopes can still exhaust the host, and the kernel may kill
the agent itself on a limit breach. This does not install systemd-oomd or
automatically size limits. See resource isolation
for supported classes, inspection, failure behavior, and disabling the profile.
Dry-Run Swarm Simulation
Before launching any real swarm, ask ACFS for a queue-aware plan:
acfs swarm plan --agents 25 --profile balanced --workload standardThe planner reads the local swarm status and capacity model, incorporates RCH
queue pressure, active tmux/NTM sessions, Beads in-progress counts, and host
resource headroom, then prints a pass/warn/fail recommendation. It is advisory
only: it does not launch agents, mutate Beads, send Agent Mail, force-release
reservations, or run build commands. JSON output is available with --json,
and fixture replay is available with --status-file.
For multi-host planning, keep a local redacted inventory at
~/.acfs/swarm/hosts.inventory.json:
acfs swarm inventory report
acfs swarm inventory validate --json
acfs swarm inventory export --format json --output inventory.redacted.json
acfs swarm inventory import --input inventory.redacted.jsonThe inventory commands are local and advisory. They read or write JSON files, preserve unknown fields for future versions, reject sensitive field names such as hostnames, IPs, keys, tokens, passwords, and home paths, and never SSH, launch NTM, run RU, send Agent Mail, mutate Beads, or change RCH config.
For each agent you plan to launch, generate a bounded startup packet from the selected Bead plus current repo instructions and bounded CM/CASS context:
acfs swarm packet --bead bd-1234 --agent-name BlueLake --role implementation
acfs swarm packet --json --bead bd-1234 --agent-name BlueLakeThe packet is designed for NTM prompt injection. It prioritizes live AGENTS.md,
README.md, Beads, and Agent Mail state over memory-derived hints, includes drift
checks, and preserves exact bv --robot-*, br, Agent Mail MCP, rch exec --,
and UBS workflow guidance. It does not claim work, reserve files, send messages,
start agents, run builds, or edit generated files. CASS retrieval uses lexical
search with --no-maintenance and includes session snippets. Live cm context
can update memory/history caches, so packets that invoke it report
safety.read_only: false. For read-only generation, use --no-live-context;
saved --cm-file and --cass-file context remains available in that mode.
Automatic packet and assignment reads use br --no-db to read exported JSONL
without opening or importing into the tracker database. Run br sync --flush-only
explicitly before generating packets when you have unexported Beads changes.
Before launching a large real swarm, ACFS can run an offline simulation of the control plane:
acfs swarm simulateThe default simulation runs 10, 25, and 50 logical-agent scenarios without launching tmux sessions, model CLIs, Beads mutations, Agent Mail writes, or local CPU-heavy builds. It writes artifacts for each scenario: generated launch plan, telemetry JSON, capacity/resource sample, timing, and pass/fail summary. Treat this as a local readiness harness, not a substitute for provider factory tests on real VPS hosts.
After one or more simulation runs, calibrate the static capacity assumptions against those local artifacts:
acfs swarm calibration --artifact-dir ~/.acfs/logs/swarm-simulations
acfs swarm calibration --json --artifact-dir ./swarm-artifacts --rch-file ./rch-timing.jsonThe calibration report is read-only. It classifies the local evidence as conservative, aligned, or too aggressive, handles missing or partial artifacts with warnings, and never changes capacity defaults, RCH state, NTM sessions, Beads, or Agent Mail.
Philosophy
The Flywheel
The "Agentic Coding Flywheel" is a virtuous cycle:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Better Environment → More Agent Productivity → │
│ More Code Written → Better Understanding → │
│ Better Prompts → Better Environment │
│ │
└─────────────────────────────────────────────────────────────────┘ACFS kicks off this flywheel by providing the best possible starting environment for agentic coding.
Design Principles
-
Beginner-Friendly, Expert-Fast: The wizard guides beginners; the one-liner serves experts.
-
Vibe-First: Optimize for velocity in throwaway environments. Safety features exist in safe mode.
-
Idempotent: Re-run without fear. The installer handles already-installed tools gracefully.
-
Single Source of Truth: The manifest defines module metadata and generated module logic; hand-written
install.showns production orchestration and dispatch. -
Security by Default: HTTPS enforcement, checksum verification, no blind
curl | bash. -
Modern Defaults: Latest versions, modern tools, optimal configurations out of the box.
The Vibe Coding Manifesto
"Vibe coding" isn't just a catchy name—it's a philosophy about how humans and AI should collaborate on software development.
What Is Vibe Coding?
Vibe coding is the practice of directing AI agents to write code while you focus on intent, architecture, and quality. Instead of typing every line yourself, you:
- Describe what you want in natural language
- Review and guide the agent's output
- Iterate rapidly through multiple approaches
- Ship faster while maintaining quality
The "vibe" comes from the flow state you enter when you're no longer fighting syntax, boilerplate, or implementation details—you're just vibing with your AI partner.
The Three Laws of Vibe Coding
1. Velocity Over Ceremony
Traditional development is ceremony-heavy: create branch, write tests first, implement, refactor, write docs, create PR, wait for review, merge, deploy. Each step has friction.
Vibe coding inverts this: ship fast, iterate faster. The AI handles boilerplate while you focus on the 10% that requires human judgment.
Traditional: Think → Plan → Implement → Test → Document → Ship
Vibe: Describe → Generate → Verify → Ship → Iterate2. Throwaway Environments Enable Boldness
The magic of vibe coding happens on ephemeral VPS instances. When your environment is disposable:
- You can experiment without fear
- Catastrophic failures are just "rebuild the VPS"
- Agents can have dangerous permissions (they can't break what's disposable)
- You focus on output, not on protecting your setup
This is why ACFS's "vibe mode" enables passwordless sudo and dangerous agent flags—on a $5/month throwaway VPS, there's nothing worth protecting.
3. Multi-Agent Is The Default
One agent is useful. Three agents working in parallel are transformative.
Vibe coding assumes you'll run multiple agents simultaneously:
- Claude for complex reasoning and architecture
- Codex for rapid prototyping and refactoring
- Antigravity for documentation and research
ACFS provides the coordination layer (Agent Mail, NTM, SLB) that makes this practical.
The Anti-Patterns
Vibe coding is NOT:
- Blindly accepting agent output without review
- Abandoning tests and quality standards
- Ignoring security on production systems
- Treating agents as replacements for understanding
The goal is augmented human judgment, not abdicated human judgment.
When NOT to Vibe Code
- Production systems with real users
- Security-critical infrastructure
- Anything involving credentials or secrets
- Long-running servers (use safe mode)
- Shared team environments (use coordination tools)
Vibe coding is for greenfield development, prototyping, experimentation, and learning. Use ACFS's safe mode for everything else.
State Machine & Checkpoint System
ACFS implements a robust checkpoint-based state machine that enables reliable resume-from-failure. This section explains how it works under the hood.
State File Format
Progress is tracked in ~/.acfs/state.json:
{
"schema_version": 3,
"started_at": "2024-12-21T10:30:00Z",
"last_updated": "2024-12-21T10:45:23Z",
"mode": "vibe",
"completed_phases": ["user_setup", "filesystem", "shell_setup"],
"partial_phases": ["cli_tools"],
"phase_attempts": {"cli_tools": 2},
"phase_steps": {
"cli_tools": {
"Installing required packages": {
"status": "running",
"required": true,
"attempt": 2,
"error": null
}
}
},
"current_phase": "cli_tools",
"current_step": "Installing ripgrep",
"failed_phase": null,
"failed_step": null,
"failed_error": null,
"skipped_phases": [],
"phase_timings": {
"user_setup": 12,
"filesystem": 8,
"shell_setup": 145
}
}Phase State Transitions
Each phase goes through a defined state machine:
┌─────────────────────────────────────────────────────────────────────────────┐
│ PHASE STATE MACHINE │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ PENDING │────▶│ RUNNING │────▶│ COMPLETE │ │
│ └──────────┘ └────┬─────┘ └──────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────┐ ┌──────────┐ │
│ │ │ FAILED │────▶│ RETRY │──┐ │
│ │ └──────────┘ └──────────┘ │ │
│ │ ▲ │ │
│ │ └────────┘ │
│ │ │
│ └──────────────────────▶┌──────────┐ │
│ (--skip flag) │ SKIPPED │ │
│ └──────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘Resume Logic
When the installer runs, it follows this decision tree:
def should_run_phase(phase_id):
state = load_state_file()
if phase_id in state.skipped_phases:
return SKIP # User explicitly skipped
if phase_id in state.completed_phases:
if verify_selected_modules(phase_id):
return SKIP
return RUN # Retry incomplete work with per-component verificationStarting a phase removes its completion checkpoint and records it as partial. Each step records whether it is required and whether it is running, successful, or failed. A zero exit status from the enclosing phase cannot override a failed required step. Completion requires clean results from the current attempt and successful module verification; only then can earlier failures be reconciled. An interrupted required step remains pending recovery in the next process.
Atomic State Updates
State file updates are atomic to prevent corruption from interrupted writes:
# Write to temp file first
echo "$new_state" > "$state_file.tmp.$$"
# Atomic rename (POSIX guarantees this is atomic on same filesystem)
mv "$state_file.tmp.$$" "$state_file"This ensures the state file is never partially written, even if the process is killed mid-update.
Recovery from Common Failures
| Failure Type | Detection | Recovery |
|---|---|---|
| Network timeout | curl exit code 28 | Retried on a fixed 0s/5s/15s schedule |
| APT lock held | apt-get waits via DPkg::Lock::Timeout=120 | Waits up to 120s for unattended-upgrades; on timeout the step fails and a re-run resumes the install |
| Disk full | df check before write | Abort with clear error |
| Out of memory | OOM killer | Resume picks up from last phase |
| SSH disconnect | N/A (session dies) | Resume on reconnect |
| Ctrl+C | Trap handler | Clean exit, state preserved |
Phase Timings & Performance
The state file tracks how long each phase takes. This enables:
- Accurate progress estimation ("Phase 4/9, ~3 minutes remaining")
- Performance regression detection across ACFS versions
- Identifying slow phases that need optimization
Error Handling & Recovery Patterns
ACFS is designed to fail gracefully and recover automatically. This section documents the error handling patterns used throughout the codebase.
The Try-Step Pattern
Every installation step is wrapped in a try_step function that captures errors without aborting:
try_step "Installing ripgrep" install_ripgrepThis pattern provides:
- Context tracking: Errors include step name, not just exit code
- Graceful continuation: Non-critical failures don't abort the whole install
- Structured reporting: Failures are collected and reported at the end
Network Resilience
Downloads retry on a fixed schedule rather than exponential backoff: retry_with_backoff in scripts/lib/error_tracking.sh (and the installer's own ACFS_CURL_RETRY_DELAYS) retries three times with 0s, 5s, and 15s delays, and only for curl exit codes that indicate a transient network problem (DNS, connect, timeout, SSL handshake, empty reply). Permanent failures such as a 404 are not retried. The Ubuntu upgrade library is the one place with true exponential backoff (ubuntu_retry_with_backoff, 30s doubling) because do-release-upgrade mirrors are slow to recover.
APT Lock Handling
The most common installation failure on a fresh VPS is APT lock contention: unattended-upgrades runs for a few minutes after first boot. To prevent transient lock errors, the installer passes -o DPkg::Lock::Timeout=120 to apt-get calls so it waits up to 120 seconds for unattended-upgrades to finish. acfs update similarly waits (up to 120 seconds) before its apt operations, and acfs doctor --fix can disable stuck unattended-upgrades runs.
Graceful Degradation
When a non-critical tool fails to install, ACFS continues with a warning:
Category: Critical → Failure aborts installation
Standard → Failure logged, installation continues
Optional → Failure noted, no warning
Examples:
Critical: bun, zsh, git (can't proceed without these)
Standard: ast-grep, lazygit (nice to have, not blocking)
Optional: atuin, zoxide (pure enhancements)The Error Report
At the end of installation (or on abort), ACFS generates a structured error report:
═══════════════════════════════════════════════════════════════════════════════
INSTALLATION REPORT
═══════════════════════════════════════════════════════════════════════════════
Status: PARTIAL SUCCESS (8/9 phases completed)
✓ Completed Phases:
• User Setup (12s)
• Filesystem (8s)
• Shell Setup (2m 25s)
• CLI Tools (4m 12s)
• Languages (3m 45s)
• Agents (1m 30s)
• Cloud (2m 10s)
• Stack (5m 20s)
✗ Failed Phase: Finalize
Step: Configuring tmux
Error: tmux.conf syntax error on line 42
Suggested Fix:
Check ~/.acfs/tmux/tmux.conf for syntax errors
Then run: curl ... | bash -s -- --yes --mode vibe --resume
═══════════════════════════════════════════════════════════════════════════════Troubleshooting Guide
This section covers common issues and their solutions. For quick debugging, start with acfs doctor.
Installation Fails Immediately
Symptom: Installer exits within seconds of starting.
Common Causes & Solutions:
| Cause | Detection | Fix |
|---|---|---|
| Not running as root | "Permission denied" | sudo bash or use sudo in curl command |
| Neither Ubuntu nor Arch-family | "Unsupported OS" | ACFS supports Ubuntu 22.04+, Arch, and Omarchy |
| No internet | "curl: (6) Could not resolve host" | Check DNS, try ping google.com |
| Old bash | Syntax errors | Upgrade to bash 4+ |
Installation Failure Recovery
When the installer fails mid-way through, it provides an auto-resume hint with a precise command to continue from where it left off.
What you'll see on failure:
[ERROR] ACFS installation failed!
To debug:
1. Check the log: cat /var/log/acfs/install.log
2. If installed, run: acfs doctor (try as ubuntu)
╔══════════════════════════════════════════════════════════════╗
║ To resume installation from this point: ║
╚══════════════════════════════════════════════════════════════╝
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/.../install.sh | bash -s -- --resume --yes
Failed phase: phase_9
Failed step: install_stackKey features of the resume hint:
| Feature | Description |
|---|---|
| Pinned commit | Uses exact SHA from original run for reproducibility |
| Preserved flags | Includes all original flags (--skip-*, --mode, --strict) |
| Automatic detection | Reads failed phase/step from ~/.acfs/state.json |
| Copyable command | Ready to paste and run immediately |
Manual recovery steps:
-
Review the error:
# Check the full log cat /var/log/acfs/install.log | tail -50 # Or search for ERROR grep -i error /var/log/acfs/install.log -
Run diagnostics:
# As the target user (ubuntu) acfs doctor # If running as root sudo -u ubuntu -i bash -lc 'acfs doctor' -
Resume installation:
# Use the exact command from the failure output # Or use the generic resume command: curl -fsSL https://acfs.sh | bash -s -- --resume --yes --mode vibe -
Check state file (advanced):
# View current installation state cat ~/.acfs/state.json | jq . # See the stored resume hint jq '.resume_hint' ~/.acfs/state.json
Common failure scenarios:
| Scenario | Typical Cause | Recovery |
|---|---|---|
| Network timeout | Transient connectivity | Wait, then resume |
| APT lock held | Unattended-upgrades | Wait 2-3 min, resume |
| Disk full | Insufficient space | Free space, resume |
| SSH disconnect | Session timeout | Reconnect, resume |
| Tool install failed | Upstream unavailable | Check status, resume |
APT Lock Errors
Symptom: E: Could not get lock /var/lib/dpkg/lock-frontend
Solutions:
-
Wait for unattended-upgrades (most common on fresh VPS):
# Check what's holding the lock sudo lsof /var/lib/dpkg/lock-frontend # Wait for it to finish (usually 2-3 minutes on fresh VPS) # Then re-run installer -
Inspect and recover if waiting doesn't help:
sudo fuser -v /var/lib/dpkg/lock-frontend || true sudo systemctl status unattended-upgrades --no-pager || true # If it still looks stuck after several minutes, reboot the VPS, # reconnect, then repair interrupted package configuration: sudo dpkg --configure -a sudo apt-get update
Install Logs & Summary JSON
Every ACFS install run produces two artifacts for debugging and tooling:
Log File Location:
~/.acfs/logs/install-YYYYMMDD_HHMMSS.logThe log file captures all stderr output from the installer, with:
- Header containing version, date, and mode
- All progress messages and errors
- ANSI colors stripped after completion
- Footer with completion timestamp
Summary JSON Location:
~/.acfs/logs/install_summary_YYYYMMDD_HHMMSS.jsonSummary JSON Schema (v1):
{
"schema_version": 1,
"status": "success", // "success" or "failure"
"timestamp": "2026-01-27T...", // ISO 8601
"total_seconds": 1200, // Wall clock time
"environment": {
"acfs_version": "0.9.0",
"mode": "vibe",
"ubuntu_version": "25.04",
"target_user": "ubuntu",
"target_home": "/home/ubuntu"
},
"phases": [
{"id": "phase_0", "duration_seconds": 5},
{"id": "phase_1", "duration_seconds": 45},
// ... completed phases in order
],
"failure": null, // null on success, or:
// "failure": {
// "phase": "phase_9",
// "step": "install_stack",
// "error": "curl failed with exit code 7",
// "resume_hint": "curl -fsSL ... | bash -s -- --resume --yes"
// }
"log_file": "/home/ubuntu/.acfs/logs/install-20260127_120000.log"
}Accessing logs:
# Find the latest log
ls -lt ~/.acfs/logs/install-*.log | head -1
# Find the latest summary
ls -lt ~/.acfs/logs/install_summary_*.json | head -1
# Parse summary JSON
jq . ~/.acfs/logs/install_summary_*.json | head -1
# Get failed phase (if any)
jq '.failure // "No failure"' ~/.acfs/logs/install_summary_*.json | tail -1
# Get phase timings
jq '.phases[] | "\(.id): \(.duration_seconds)s"' ~/.acfs/logs/install_summary_*.json | tail -1Sharing logs for support:
# Create a support bundle (strips sensitive data)
acfs support-bundle > support-bundle.txt
# Or manually share (review for secrets first):
cat ~/.acfs/logs/install-*.log | tail -200 # Last 200 lines
cat ~/.acfs/logs/install_summary_*.json | tail -1 # Latest summarySupport Bundle Command
The acfs support-bundle command collects all diagnostic data into a single archive for troubleshooting.
Usage:
acfs support-bundle [options]Options:
| Option | Description |
|---|---|
--verbose, -v | Show detailed output during collection |
--output, -o DIR | Output directory (default: ~/.acfs/support) |
--no-redact | Disable secret redaction (WARNING: bundle may contain secrets) |
--help, -h | Show help |
Output files:
~/.acfs/support/<run>/diagnostics/ # Unpacked bundle directory
~/.acfs/support/<run>/diagnostics.tar.gz # Compressed archive (shareable)
~/.acfs/support/<run>/diagnostics/manifest.json # Bundle manifestEach run uses a unique private directory. Existing archive files or symlinks are never overwritten; if compression fails, the command prints the unpacked diagnostic directory instead. Review the bundle before sharing it.
What's collected:
| File | Description |
|---|---|
state.json | Installation state and checkpoints |
VERSION | ACFS version |
checksums.yaml | Upstream verification checksums |
logs/install-*.log | Recent install logs (up to 10) |
logs/install_summary_*.json | Recent install summaries |
doctor.json | Health check results |
credential_status.json | Passive credential-source metadata; authentication remains unverified |
versions.json | Installed tool versions |
environment.json | OS, memory, disk, user info |
os-release | Linux distribution info |
journal-acfs.log | Systemd journal for ACFS services |
config/.zshrc | Shell configuration |
Security & Redaction:
By default, sensitive data is automatically redacted:
| Pattern | Example | Redacted To |
|---|---|---|
| OpenAI API keys | sk-abc123... | <REDACTED:api_key> |
| AWS keys | AKIAIOSFODNN... | <REDACTED:aws_key> |
| GitHub tokens | ghp_xxxx... | <REDACTED:github_token> |
| Vault tokens | hvs.xxxx... | <REDACTED:vault_token> |
| Slack tokens | xoxb-xxxx... | <REDACTED:slack_token> |
| Bearer tokens | Bearer xxx... | Bearer <REDACTED:bearer> |
| JWTs | eyJhbGc... | <REDACTED:jwt> |
| Passwords | "password": "..." | "password": "<REDACTED:password>" |
| Private key blocks | -----BEGIN ... PRIVATE KEY----- | <REDACTED:private_key> |
Quoted credentials are masked as complete values, including escaped quotes and JSON-encoded diagnostic strings. Public numeric identifiers retain their original bytes. If redaction fails, bundle creation stops.
Credential lifecycle guide
Start by choosing which providers your project needs. ACFS installs tools without requiring credentials for every service. A missing optional credential is normal; make a provider required only when your workflow will use it. Keep API keys, OAuth access tokens and refresh tokens in a secret store or the provider's own credential storage. Never paste them into commits, support logs, chat or command arguments.
Follow this sequence: inspect → configure → validate → refresh or rotate. The passive checks below cover inspect/configure readiness. Provider acceptance and permissions require a separate live check.
Inspect readiness and declare requirements
Inspect local credential metadata without running login, whoami, setup steps
or provider requests:
acfs services-setup --credential-status
acfs services-setup --credential-status --require vercel --require supabase
onboard credential-statusThe version-1 acfs.credential_status JSON covers Claude, Codex, Gemini, Vercel,
Supabase and Wrangler. Each entry reports state, source, requirement,
authentication and a remediation ID. All services are optional unless named
with repeatable --require; unmet presence requirements return exit 1. Exit 0
means the declared storage requirements are met, not successful authentication.
Invalid options, unresolved target context or missing jq return exit 2.
States distinguish missing, placeholder, shell_configured, file_configured,
expired, refresh_required, vault_backed and unknown. An environment placeholder takes
precedence over a cached file
so a shadowing export stays visible. Malformed JSON or credential shapes,
unreadable or oversized files (over 1 MiB), invalid TOML and invalid expiry
metadata report unknown. Every entry
explicitly marks authentication unverified; a future expiry does not prove a
token is accepted or has the permissions your project needs.
For cached Claude OAuth credentials, claudeAiOauth.expiresAt is interpreted as
milliseconds since the Unix epoch. Gemini OAuth uses expiry_date in the same
units. A stored access token with a past expiry reports expired, including when
a refresh token is present. A refresh-only file reports refresh_required.
Both use refresh_or_reauthenticate: let the provider CLI refresh or sign in
again. The report never sends a refresh request, rotates a token or modifies the
credential file. Refresh-token presence does not prove refreshability.
Doctor marks both states as warnings. Onboarding keeps these installed services
in its attention list and excludes them from the configured-service count.
For JSON credential files, absent or null expiry preserves an unverified stored
candidate. Present expiry
must be a nonnegative integral number no later than year 9999; strings, negative
values, fractions and oversized numbers report unknown for stored candidates.
Placeholder access tokens remain placeholders. Exported API keys keep their
environment precedence. --require cannot pass on expired or refresh-only
credentials. The same lifecycle states reach normal service setup, doctor,
support bundles and onboarding.
| Provider / credential | Local expiry evidence | Meaning and recovery |
|---|---|---|
| Claude OAuth | claudeAiOauth.expiresAt, epoch milliseconds | Past: expired; refresh-only: refresh_required; use Claude's sign-in/refresh flow |
| Codex OAuth | Unverified JWT payload exp, epoch seconds, from the stored access token | Past: expired; refresh-only: refresh_required; use Codex's auth flow; last_refresh is not an expiry |
| Gemini OAuth | expiry_date, epoch milliseconds | Past: expired; refresh-only: refresh_required; use Gemini's sign-in/refresh flow |
| Vercel OAuth | expiresAt, epoch seconds, with token and refreshToken | Past: expired; refresh-only: refresh_required; run Vercel's auth flow |
| Wrangler OAuth | TOML oauth_token, refresh_token, RFC3339 string expiration_time | Past or absent OAuth expiry: expired, matching Wrangler; refresh-only: refresh_required |
| Supabase PAT and exported API keys | No reliable local expiry field | Storage remains unverified; the provider decides validity, expiry and permissions |
| Vault reference | Declared source, without fetching the value | vault_backed; resolve through your approved Vault workflow before validation |
JWT inspection checks the payload's syntax and bounded integral expiry. It does
not verify the signature, issuer, audience or permissions. A future or absent
claim is not proof of authentication. API-key fields do not inherit expiry from
JWT-looking text. Invalid claims remain unknown; private claims never appear
in reports. Wrangler uses Python 3.11+ tomllib in an isolated, local stdin-only
parser. If that parser is unavailable, the state remains unknown; ACFS does not
install a parser or evaluate the TOML as shell code. Duplicate keys and malformed
calendar/offset values are rejected. Wrangler v1's retired file api_token is
unknown; migrate it to the current provider-supported environment workflow.
Current Vercel file storage is checked in its application-data directory
(~/.local/share/com.vercel.cli on Linux, honoring absolute XDG_DATA_HOME, or
~/Library/Application Support/com.vercel.cli on macOS), then the existing ACFS
and legacy credential locations. Codex honors CODEX_HOME. The first nonmissing
file state wins: an expired or malformed current file is not hidden by an older
stored token. Keyring-only credentials and custom provider --global-config
locations may be invisible to passive checks; that does not mean you are logged
out. Use a provider check in the exact project/account context.
Wrangler prefers an existing ~/.wrangler directory; otherwise it uses
$XDG_CONFIG_HOME/.wrangler, defaulting to ~/.config/.wrangler on Linux or
~/Library/Preferences/.wrangler on macOS. missing describes the inspected
environment and files, not every provider's native credential store.
Configure the credential your project actually needs
| Provider | Interactive setup | Headless / CI setup |
|---|---|---|
| Claude | claude auth login, or follow onboarding's Claude login prompt | Inject ANTHROPIC_API_KEY through your secret manager |
| Codex | codex login; codex login --device-auth on a headless host | Use your approved API-key workflow; feed an existing OPENAI_API_KEY to codex login --with-api-key over stdin if persisting it is intended |
| Gemini | Launch Gemini and choose its Google sign-in flow | Inject GEMINI_API_KEY or GOOGLE_API_KEY |
| Vercel | vercel login | Inject VERCEL_TOKEN |
| Supabase | supabase login | Inject SUPABASE_ACCESS_TOKEN |
| Cloudflare | wrangler login | Inject CLOUDFLARE_API_TOKEN with only the needed account/resource permissions |
For interactive setup, run acfs services-setup, or run onboard and choose its
service-authentication menu. Re-run the passive report afterward. A provider-rejected export can
shadow a fresh file; a placeholder export always shadows the cached credential.
Unset the shadowing variable in the current shell if file authentication is
intended. Update its secret-manager/CI/startup-file source too, so a new shell
does not restore the obsolete value. Startup files are parsed as text by ACFS,
never sourced to discover secrets.
For CI, inject secrets into the job environment, request only the providers the
job needs, and run the passive --require check before deployment. Then perform
the relevant live permission check. An exit0 presence check cannot prove that a
token can deploy, administer a database or access a particular account.
Validate provider acceptance separately
| Provider | Useful check | What it proves / limits |
|---|---|---|
| Claude | claude auth status, then a provider-approved account/profile check | CLI status can be local only; account acceptance needs an actual provider response |
| Codex | codex login status, then an account/usage check for OAuth or the OpenAI models-list API for an API key | Login status alone is local; no model request is needed for an auth probe |
| Gemini | Google account/userinfo for OAuth, or Gemini models-list API for a key | Confirms that endpoint's access, not quota or permissions for every model |
| Vercel | vercel whoami or Vercel's current-user API | Confirms the selected account; project/team authorization may still differ |
| Supabase | supabase projects list or Management API projects-list | Confirms the token's permitted project listing, not database or migration permission |
| Cloudflare | wrangler whoami, or token verification / permitted account listing | Token-active status and account/resource permissions are distinct |
Provider CLIs may refresh OAuth credentials or write their own caches during these checks. Use direct, bounded GET requests to a fixed official endpoint when credential files must stay unchanged. Keep credentials in request headers in memory; do not put them in URLs or process arguments. Do not follow redirects with an authorization header. Retain only provider/probe names, HTTP status and fixed outcome categories. Discard response bodies containing account IDs, emails, project names, tokens or quota details. Do not use a paid model request as a login test.
A 401 is authentication rejection for that endpoint. A 403 can mean missing scope, account restrictions or a network/security layer; it does not establish that a token expired. DNS/TLS/timeout errors establish no auth conclusion. A successful account-list response does not prove deployment permission. Record the endpoint, context and observed result separately from passive source state.
Refresh, reauthenticate, rotate and revoke
- For
expiredorrefresh_required, let the provider CLI perform its supported refresh/sign-in flow. ACFS's passive checks never send refresh requests. Refresh-token presence does not prove that refresh will succeed. If refresh is rejected, sign in again; do not repeatedly retry a revoked/reused token. - For
placeholder, replace or unset the source before using the service. Forunknown, inspect the local format/permissions and parser availability privately; do not paste the credential file into support. - For API-key/PAT rotation, create a least-privilege replacement through the provider, update the secret store and each job/environment that consumes it, validate the required operation, then revoke the old credential. Test existing sessions and new shells so a stale export cannot keep the old key active.
- If a credential was exposed, use the provider's incident/revocation process. Removing a value from a working tree does not revoke it or erase Git history. Coordinate cleanup explicitly rather than deleting files or rewriting history as an automatic ACFS remediation.
Vault-backed credentials
Declare unresolved Vault provenance with ACFS_<SERVICE>_VAULT_REF, where
SERVICE is CLAUDE, CODEX, GEMINI, VERCEL, SUPABASE or WRANGLER:
export ACFS_CLAUDE_VAULT_REF='vault://secret/acfs/claude#api_key'
acfs services-setup --credential-status --require claudeThis is an ACFS annotation, not a URI understood by Vault or the provider.
It names a KV mount, secret path and field. Components use ASCII letters,
digits, _, - and .; empty, ./.., placeholder components and references
over 2048 characters are rejected as unknown. The annotation is optional.
VAULT_ADDR, a Vault installation or a Vault token alone does not establish
provider provenance. The collector never contacts Vault, reads its token helper,
prints the reference or fetches a secret. Valid references report vault_backed,
source=vault, authentication=unverified, resolve_vault_reference and do
not satisfy --require. Doctor warns and onboarding keeps the service in its
attention list. Materialize the secret through your approved Vault workflow
into the provider environment or credential file, then validate provider access.
A nonblank active provider export takes precedence, including placeholders.
Without one, an explicit Vault annotation takes precedence over cached files.
An actual exported key therefore overrides the annotation; remove the annotation
when switching to file-based credentials. Literal vault://... references in
provider token fields are also unresolved and cannot count as usable tokens.
The annotation does not prove the secret exists or that Vault access is permitted.
TypeScript onboarding exposes getCredentialStatus(requiredServices) with the
same JSON metadata, state/remediation vocabulary and requirements as Bash.
Both honor CODEX_HOME for Codex's auth.json. checkAllServices() attaches
canonical credential metadata to its provider entries. Stored candidates use
configured: true with authenticated: false; successful live checks may use
authenticated: true. Expired, refresh-only, placeholder, invalid and unresolved
Vault candidates cannot count as configured. Startup-file assignments are
still parsed as text by the interactive checks, never evaluated; the passive
report only examines exported variables and credential files.
The JSON report reuses the existing local credential paths and placeholder policy,
omits secret values and file paths, and does not change credential files. It
cannot be combined with setup flags such as --yes or --install-claude-guard.
acfs support-bundle includes the same optional-service metadata in
credential_status.json, its manifest, and the linked support report. It checks
the resolved target user's credential files and the collecting process's exported
environment, without invoking provider commands for this capture. Only the fixed
state/source/remediation vocabulary is retained; extra fields, tokens and paths
are omitted even with --no-redact. Failed, timed-out or invalid captures produce
a warning record rather than an authentication claim.
Normal acfs doctor output also includes these credential-source states, and
acfs doctor --json exposes them as credential.source.<service> checks. Missing
optional credentials are skipped; placeholders and unknown storage formats are
warnings. Stored candidates are reported with authentication explicitly
unverified and the same remediation IDs as the passive report. This storage
check performs no login, refresh, whoami or provider validation; --deep
continues to request doctor's separate functional checks.
The Bash onboarding authentication screen shows the same six source states,
remediation IDs and next steps. Its existing service indicators mean configured
or checked; stored tokens remain explicitly unverified. onboard credential-status
returns the optional-service JSON without running provider commands or recording
tutorial progress. It uses onboarding's resolved target home. Failed, invalid or
timed-out captures return a constant error record and exit 2; raw output is never
shown. This command accepts no setup flags or --require options; use
acfs services-setup --credential-status --require SERVICE for presence requirements.
Credential JSON files must contain exactly one object. Concatenated documents
and raw or JSON-escaped NUL bytes remain unknown and cannot satisfy a declared
storage requirement. The byte bound and parsed state use one captured snapshot,
including its trailing newlines. Rejected
webhook URLs are logged without echoing the URL or any credentials it contains.
The read-only acfs rescue --json advisor also requires single-object checkpoint
and doctor reports. Invalid phase lists, schema versions, or diagnostic counters
cannot produce healthy advice. Timing overrides use bounded nonnegative decimal
seconds, including leading-zero values; expressions are rejected before any
arithmetic. Relative search directories are passed to filesystem tools as
absolute paths so option-like names remain filenames.
Status JSON for editor integrations
acfs status --ide emits the version-1 acfs.editor_status contract for local
editor extensions and scripts. It reads the existing install state and checks
binary presence. It makes no network requests and runs no installed agent tools.
Add --live to query NTM/tmux through the existing swarm collector, with a
one-second timeout per tool and a five-second overall deadline. Other swarm
tools, credential stores and provider APIs are not queried.
acfs status --ide # Passive install snapshot
acfs status --ide --live # Also query local session countsThe payload includes schema_version, kind, status, generated_at,
freshness, install, provider, sessions, collectors, redaction,
commands, next_actions and support. Clients should reject unknown schema
versions and ignore additional fields within version 1. Exit codes are 0 for
OK, 1 for incomplete state/tools or unavailable requested session data, and 2
for failed, missing or invalid install state. A missing jq dependency returns
2 with a minimal versioned envelope and error: "jq_unavailable".
Every invocation collects a new snapshot; freshness.cache is "none".
state_updated_at is the validated persisted timestamp, not the query time.
Missing or failed session queries have null counts, not zero. Provider
names come only from recognized provider/vps_provider values in local state;
missing or unrecognized metadata remains unknown. The phase-completion summary
describes recorded install progress; tool counts describe executable presence,
not authenticated or operational tools.
Raw state, errors, session names, usernames, paths, addresses, provider IDs and
credentials are omitted. redaction.count counts recognized sensitive field
names omitted from state; it is not a count of validated or leaked credentials.
Command snippets are argument arrays from a fixed catalog. They are advisory:
execute_automatically is false, and the support-bundle hint explicitly says
that creating a bundle writes files. --ide rejects --check-updates and
--short; the existing human, short and --json status formats remain available.
These selected-field examples are checked against the CLI regression fixtures:
{"scenario":"complete","status":"ok","install_status":"complete","sessions_status":"not_requested","session_count":null}
{"scenario":"partial","status":"warn","install_status":"partial","sessions_status":"not_requested","session_count":null}
{"scenario":"failed","status":"error","install_status":"failed","sessions_status":"not_requested","session_count":null}
{"scenario":"live","status":"ok","install_status":"complete","sessions_status":"ok","session_count":2}Before launching a large agent swarm or sharing a support bundle, run a local credential preflight:
acfs credential-preflight --jsonThe preflight scans bounded ACFS state/log files plus shell config/history surfaces and reports only categories, counts, file labels, and remediation guidance. It never prints raw secret values or snippets.
Numeric passwords and literals containing words like example or dummy are
checked under credential fields. Complete placeholder markers, environment
references, and empty parameter defaults stay exempt; a plaintext parameter
default is checked as a literal. Documented numeric token counters and limits
are classified by their field names. The scan reads expressions as data and
does not execute them or verify provider authentication or credential expiry.
Example workflow:
# Create support bundle
acfs support-bundle
# Output: ~/.acfs/support/20260127_120000.tar.gz
# Share the archive when filing an issue
# The archive is safe to share (secrets redacted)Disable redaction (use with caution):
# WARNING: Bundle may contain API keys, tokens, and passwords
acfs support-bundle --no-redactWhen to use:
- Installation failed and you need to share logs
- Filing a GitHub issue about ACFS
- Diagnosing tool installation problems
- Sharing system state with support
Shell Not Changing to zsh
Symptoms: Still seeing a Bash prompt, shell setup refuses to complete, or a previous installation selected an unlisted shell alias.
Keep the current session open while repairing the account. Inspect the
effective account record instead of $SHELL, which can describe an older
session. Run these checks as the affected user so the executable check uses that
account's permissions:
account="$(id -un)"
login_shell="$(getent passwd "$account" | cut -d: -f7)"
printf 'Login shell for %s: %s\n' "$account" "$login_shell"
[[ "$login_shell" == /* && -f "$login_shell" && -x "$login_shell" ]] &&
grep -aFxq -- "$login_shell" /etc/shellsThe configured path must be executable by that account and appear exactly in
/etc/shells. On Arch/usrmerge, /usr/sbin/zsh and /usr/bin/zsh can reach the
same binary while only one spelling is approved. Authentication using
pam_shells can reject the
unlisted spelling; see issue #473.
ACFS's shell.login module selects an approved executable path, preserves the
original login shell, applies the change, and verifies both the local and NSS
account records. It reports failure if no acceptable shell can be established,
the approval file is missing or unsafe, privileges are insufficient, or the
change cannot be verified. It does not add entries to /etc/shells. An
administrator must repair an invalid host approval policy before retrying.
To repair a previous installation using the current verified installer, rerun
the account-shell module and its dependencies. The explicit target keeps a
root-run repair attached to the intended account. From an administrator session,
set account to the affected Linux username; when already root, omit sudo:
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh |
sudo env TARGET_USER="$account" bash -s -- --yes --mode safe --force-reinstall --only shell.loginBefore the first local shell change, ACFS creates .shell.pre-acfs in the
account's home with permissions 0600. Retries preserve that first snapshot.
This is a recovery record, not an automatically approved restore target: it
may contain the previously invalid /usr/sbin/zsh alias. Validate any proposed
manual restore path against /etc/shells and the account's executable access
before using it. When a change fails after writing, ACFS attempts to restore an
approved original shell while the account identity remains unchanged, then
verifies the result and still reports the installation failure.
For externally managed users, ACFS leaves the provider's login shell unchanged. An approved Bash login can receive a verified interactive zsh handoff; an unapproved or unsupported provider shell requires an identity-provider fix. After successful repair, repeat the account checks above and test a new login before closing the recovery session. An existing Bash session stays Bash until you start a new shell or log in again.
Agent Authentication Issues
Start with the safe readiness report:
bash scripts/agent-readiness-audit.sh --jsonClaude Code:
# Check auth status
claude --version
ls -la ~/.claude/ # or ~/.config/claude/
# Re-authenticate
claude # Follow prompts, then use /login inside Claude Code to switch accountsCodex CLI:
# Check auth status
codex --version
# Re-authenticate (uses ChatGPT account, not API key)
codex # Follow first-run sign-in promptsAntigravity CLI:
# Check auth status
agy --version
# Re-authenticate
agy # Follow Google login flow"Command Not Found" After Install
Symptom: claude: command not found even though install succeeded.
Solutions:
-
Reload shell config:
source ~/.zshrc # Or start a new shell exec zsh -
Check PATH:
echo $PATH | tr ':' '\n' | grep -E "(bun|local|cargo)" # Should include: ~/.bun/bin, ~/.local/bin, ~/.cargo/bin -
Manual path fix:
export PATH="$HOME/.bun/bin:$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
Doctor Shows Missing Tools
Symptom: acfs doctor shows failed checks for tools you expected to be installed.
Understanding doctor output:
Doctor checks are generated directly from the manifest, so they verify the exact same tools the installer provides. When a check fails, doctor shows a copy-pasteable fix command:
✗ tools.lazygit - Lazygit terminal UI not found
Fix: curl -fsSL https://agent-flywheel.com/install | bash -s -- --yes --force-reinstall --only tools.lazygitSolutions:
-
Re-run the specific module (use the fix suggestion):
curl -fsSL https://agent-flywheel.com/install | bash -s -- --yes --force-reinstall --only tools.lazygit curl -fsSL https://agent-flywheel.com/install | bash -s -- --yes --force-reinstall --only lang.go curl -fsSL https://agent-flywheel.com/install | bash -s -- --yes --force-reinstall --only stack.dcg -
Re-run an entire phase (for multiple failures in one category):
curl -fsSL https://agent-flywheel.com/install | bash -s -- --yes --force-reinstall --only-phase cli curl -fsSL https://agent-flywheel.com/install | bash -s -- --yes --force-reinstall --only-phase stack -
Run auto-fix mode (applies safe, deterministic fixes):
acfs doctor --fix acfs doctor --fix --dry-run # Preview fixes first
Note: Doctor checks match the manifest verify commands exactly. If a tool was skipped during installation (e.g., using --mode safe), the check will fail. This is expected—run acfs doctor to see which tools are missing and decide which to install.
Tmux Configuration Errors
Symptom: Tmux won't start or shows config errors.
Solutions:
-
Check syntax:
tmux source-file ~/.tmux.conf # Will show line number of any errors -
Reset to ACFS defaults:
cp ~/.acfs/tmux/tmux.conf ~/.tmux.conf -
Version mismatch (old tmux, new config):
tmux -V # Check version # ACFS config requires tmux 3.0+
Stack Tools Not Working
Symptom: ntm, slb, dcg, etc. not found or erroring.
Solutions:
-
Reinstall stack:
acfs update --stack --force -
Check cargo install worked:
ls ~/.cargo/bin/ # Should contain ntm, slb, ru, etc. ls ~/.local/bin/ # dcg often installs here -
Rust not in path:
source ~/.cargo/env
DCG Hook Issues
Symptom: DCG isn't blocking commands or Claude reports hook errors.
Solutions:
-
Run the built-in health check:
dcg doctor -
Re-register the hook:
dcg install --force -
Verify hook registration:
grep -n dcg ~/.claude/settings.json ~/.config/claude/settings.json -
Reinstall if binary is missing:
which dcg # Should return a path # If missing, reinstall through the ACFS verified installer path: acfs update --stack-only dcg install --force # Register hook after reinstall
Complete Reset
When all else fails, the nuclear option:
# Save any important files first!
# Backup ACFS state (recommended)
ts="$(date +%Y%m%d_%H%M%S)"
[ -d ~/.acfs ] && mv ~/.acfs ~/.acfs.backup."$ts"
# Backup installed configs (optional)
for f in ~/.zshrc ~/.tmux.conf ~/.p10k.zsh; do
[ -f "$f" ] && mv "$f" "$f".backup."$ts"
done
{ acfs_installer="$(curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/main/install.sh")" || acfs_installer="$(curl -fsSL "https://cdn.jsdelivr.net/gh/Dicklesworthstone/agentic_coding_flywheel_setup@main/install.sh")"; } && printf '%s\n' "$acfs_installer" | bash -s -- --yes --mode vibe --force-reinstallSecurity Threat Model
ACFS takes security seriously while acknowledging the inherent risks of curl | bash installation. This section documents our threat model and mitigations.
What We Protect Against
| Threat | Mitigation |
|---|---|
| Man-in-the-middle (MITM) | HTTPS enforcement for all downloads |
| Compromised upstream scripts | SHA256 checksum verification |
| Malicious package injection | Official package sources only (apt, cargo, bun) |
| Credential exposure | No credentials stored in scripts or configs |
| Privilege escalation | Minimal sudo usage, explicit permission grants |
| Persistent backdoors | Ephemeral VPS model; start fresh if concerned |
What We Don't Protect Against
| Threat | Why Not | Mitigation |
|---|---|---|
| Compromised GitHub | Would require GitHub-level breach | Use release tags, verify commits |
| Compromised upstream maintainers | Can't verify humans | Trust + checksum verification |
| Zero-day in installed tools | Beyond our control | Keep tools updated, follow CVEs |
| Physical VPS access | Provider responsibility | Choose reputable providers |
| Vibe mode abuse | By design for throwaway VPS | Use safe mode on important systems |
The curl | bash Debate
The curl | bash pattern is controversial. Critics argue:
- You're executing arbitrary code from the internet
- The download could be swapped mid-stream
- You can't audit before executing
Our response:
- HTTPS prevents mid-stream swapping
- Checksums verify content matches known-good versions
- Ephemeral environments limit blast radius
- Open source allows pre-audit of install.sh
For maximum security, you can:
curl -fsSL "https://..." -o install.sh
less install.sh
bash -s -- --yes --mode vibe < install.shFeed the reviewed file to bash -s exactly as curl | bash would. Run by path
(bash install.sh), the installer expects a full repository checkout beside it.
Checksum Verification Deep Dive
Every upstream installer we fetch is verified against known-good checksums:
# checksums.yaml excerpt
installers:
bun:
url: "https://bun.sh/install"
sha256: "a1b2c3d4e5f6..."
last_verified: "2024-12-15"
notes: "Official Bun installer"The verification process:
1. Download into an installer-owned private temporary file
2. Calculate SHA256 from those staged bytes
3. Compare against the reviewed checksum metadata
4. If it matches, copy the verified bytes into a read-only staging file
5. Execute that exact staging file with a trusted Bash or sh binary
6. If any step fails or the hash differs, refuse executionA mismatch could mean:
- Upstream released a new version (common, usually safe)
- Upstream was compromised (rare, investigate before updating)
Our update process:
- Monitor upstream releases
- Review changes in new installer versions
- Update checksums only after manual review
- Commit with descriptive message explaining what changed
Vibe Mode Security Implications
Vibe mode (--mode vibe) enables:
- Passwordless sudo for ubuntu user
--dangerously-skip-permissionsfor Claude--dangerously-bypass-approvals-and-sandboxfor Codex- Always-proceed tool permission for Antigravity (
agy)
This is intentionally insecure for velocity. Use only on:
- Throwaway VPS you don't care about
- Non-production environments
- Personal experimentation
Never on:
- Production servers
- Shared team infrastructure
- Systems with sensitive data
- Long-running servers
Comparison to Alternatives
How does ACFS compare to other ways of setting up a development environment?
vs. Manual Setup
| Aspect | Manual | ACFS |
|---|---|---|
| Time | 3-7 hours | 30 minutes |
| Consistency | Varies | Identical every time |
| Documentation | Your memory | This README |
| Resume on failure | Start over | Automatic |
| Updates | Manual each tool | acfs update |
When to use manual: When you need to understand every detail, or have highly specific requirements.
vs. Dotfiles Repos
| Aspect | Dotfiles | ACFS |
|---|---|---|
| Scope | Configs only | Full tool installation |
| Portability | Mac/Linux | Ubuntu-focused |
| Maintenance | DIY | Maintained project |
| Agent focus | None | Core feature |
When to use dotfiles: When you already have tools installed and just want configs.
vs. Nix/NixOS
| Aspect | Nix | ACFS |
|---|---|---|
| Reproducibility | Perfect | Good |
| Learning curve | Steep | Gentle |
| Rollback | Native | Manual |
| Complexity | High | Low |
| Adoption | Growing | Easy |
When to use Nix: When you need perfect reproducibility and are willing to invest in learning Nix.
vs. DevContainers
| Aspect | DevContainers | ACFS |
|---|---|---|
| Isolation | Container | Full VPS |
| Resource overhead | Container runtime | None |
| IDE integration | VSCode-centric | Terminal-native |
| Agent experience | Limited | Native |
When to use DevContainers: When you want isolated project environments within an existing machine.
vs. Ansible/Terraform
| Aspect | Ansible/TF | ACFS |
|---|---|---|
| Scope | Infrastructure | Development env |
| Complexity | High | Low |
| Audience | DevOps | Developers |
| Learning curve | Steep | Gentle |
When to use Ansible/Terraform: When you're managing fleets of servers, not individual dev environments.
The ACFS Sweet Spot
ACFS is optimal when you need:
- Fast setup of a complete agentic coding environment
- Fresh Ubuntu VPS as your target
- AI coding agents as primary tools
- Throwaway/ephemeral infrastructure mindset
- Minimal configuration to get started
The Agent Flywheel Stack Philosophy
The 10-tool stack included in ACFS isn't random—each tool addresses a specific problem discovered through extensive multi-agent development experience.
The Problems
Running multiple AI coding agents simultaneously surfaces problems that don't exist with single-agent or no-agent development:
- Session chaos: Agents in random terminal windows, no organization
- File conflicts: Two agents editing the same file simultaneously
- No communication: Agents can't coordinate or share findings
- Dangerous commands: Agents running
git reset --hardorrm -rfwithout oversight - Lost context: No memory of what agents learned previously
- Auth switching: Different projects need different credentials
- History fragmentation: Agent conversations scattered across systems
- No task visibility: Hard to see what agents are working on
- Repo sprawl: Dozens of repos, hard to keep synced, uncommitted work everywhere
- Visual debugging gaps: Screenshots on phone, can't view in SSH terminal
The Solutions
Each tool in the stack addresses specific problems:
| # | Tool | Problem Solved | Philosophy |
|---|---|---|---|
| 1 | NTM | Session chaos | Named sessions create order from chaos |
| 2 | Agent Mail | No communication + file conflicts | Message-passing + file reservations |
| 3 | UBS | Dangerous commands | Guardrails with intelligence |
| 4 | Beads Viewer | No task visibility | Graph-based task dependencies |
| 5 | CASS | History fragmentation | Unified search across all agents |
| 6 | CM | Lost context | Procedural memory for agents |
| 7 | CAAM | Auth switching | One command to switch identities |
| 8 | SLB | Dangerous commands | Two-person rule for nuclear options |
| 9 | DCG | Dangerous git/fs commands | Sub-millisecond Claude Code hook blocks destructive operations |
| 10 | RU | Repo sprawl | Sync repos + AI-driven commit automation across dirty repos |
Bundled Utilities:
| Tool | Problem Solved | Philosophy |
|---|---|---|
| giil | Visual debugging gaps | Download cloud images (iCloud, Dropbox, Google Photos) to terminal |
| csctf | Knowledge capture | Convert AI chat shares to searchable Markdown/HTML archives |
The Synergy Effect
These tools are designed to work together:
NTM spawns agents → Agents register with Agent Mail →
Agent Mail reserves files → DCG blocks dangerous commands →
UBS validates operations → Beads tracks tasks →
CASS searches history → CM provides memory →
CAAM manages auth → SLB gates nuclear operations →
RU syncs repos and automates commitsNo single tool is transformative alone. Together, they enable workflows that would otherwise be impossible:
- 10 agents working in parallel without stepping on each other
- Continuous operation across SSH disconnects
- Audit trails for every agent action
- Coordination without manual intervention
- Safety without sacrificing velocity
Design Principles of the Stack
- Unix Philosophy: Each tool does one thing well
- Composition: Tools designed to pipe into each other
- Terminal-First: TUI over GUI, speed over polish
- Agent-Native: Built for AI, not adapted for AI
- Git-Friendly: All state is auditable in version control
Advanced Configuration
ACFS supports various configuration mechanisms for advanced users.
Environment Variables
| Variable | Default | Description |
|---|---|---|
ACFS_HOME | ~/.acfs | Configuration directory |
ACFS_REF | main | Git ref to install from (tag, branch, or commit SHA) |
ACFS_CHECKSUMS_REF | main (when pinned) / ACFS_REF (when branch) | Ref used to fetch checksums.yaml |
ACFS_VERIFIED_INSTALLER_CACHE | unset | acfs-installer-cache/ directory, or its parent; when set, verified installer entrypoint acquisition is fail-closed to that cache |
ACFS_LOG_DIR | /var/log/acfs | Log directory |
TARGET_USER | ubuntu | User to configure |
TARGET_HOME | Resolved from TARGET_USER | User home directory (or explicit override) |
Examples:
# Install from a tagged release (recommended for production)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/v0.1.0/install.sh" | bash -s -- --yes --mode vibe --ref v0.1.0
# Install from a specific branch (development/testing)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/feature/new-tool/install.sh" | bash -s -- --yes --mode vibe --ref feature/new-tool
# Install from a specific commit (reproducibility)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/abc1234/install.sh" | bash -s -- --yes --mode vibe --ref abc1234
# Pin installer version but use latest checksums (avoid stale hash mismatches)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup/v0.9.0/install.sh" | bash -s -- --yes --mode vibe --ref v0.9.0 --checksums-ref mainTip: Always match the URL path with
--refso the initial script and all subsequently fetched scripts come from the same ref. If you use environment variables in a pipeline, attach them tobash, notcurl:curl ... | ACFS_REF=v0.9.0 bash -s -- --yes --mode vibe. Tip: For pinned installs (tags/SHAs), checksums default tomainto avoid stale installer hashes. Override withACFS_CHECKSUMS_REFif you want checksums pinned to the same ref.
Complete Installer CLI Options
The installer supports extensive command-line customization:
Execution Control:
--yes, -y # Skip all prompts (non-interactive)
--dry-run # Simulate without making changes
--print # Print what would be installed
--mode vibe|safe # Installation mode (default: vibe)
--interactive # Force interactive mode with prompts
--strict # Abort on any error (vs. continue with warnings)
--ref <ref> # Git ref to install from (branch, tag, or commit SHA)
--checksums-ref <ref> # Fetch checksums.yaml from this ref (default: main for pinned tags/SHAs)
--verified-installer-cache <dir> # Require checksum-pinned installer entrypoints from this cache (Ubuntu only; transitive downloads still use the network)Resume & State:
--resume # Resume from last checkpoint
--force-reinstall # Ignore state, reinstall everything
--reset-state # Clear state.json and start freshUbuntu Upgrade:
--skip-ubuntu-upgrade # Suppress an explicit upgrade request (wins in any flag order)
--target-ubuntu=26.04 # Opt into upgrading to Ubuntu 26.04 LTS
--target-ubuntu 26.04 # Alternative syntaxWithout --target-ubuntu, supported Ubuntu 22.04/24.04 LTS hosts stay on their current release. Active system upgrade checkpoints still block normal installation, including when --skip-ubuntu-upgrade is set.
Skip Flags:
--skip-postgres # Skip PostgreSQL 18
--skip-vault # Skip HashiCorp Vault
--skip-cloud # Skip Wrangler, Supabase, Vercel CLIs
--skip-preflight # Skip pre-flight validationModule Selection
Fine-grained control over what gets installed using manifest-driven selection:
--list-modules # List available modules
--print-plan # Show execution plan without running
--only <module> # Only run specific module(s)
--only-phase <phase> # Only run modules in a phase
--skip <module> # Skip specific module(s)
--no-deps # Don't auto-include dependencies (⚠️ advanced)Key behaviors:
- Dependency closure:
--onlyautomatically includes required dependencies (safe by default) - Skip safety:
--skipfails early if it would break a required dependency chain - Deterministic:
--print-planshows exactly what will run, in what order
Examples: Only install agents (plus their dependencies):
curl -fsSL "..." | bash -s -- --yes --only-phase agentsSkip PostgreSQL and Vault:
curl -fsSL "..." | bash -s -- --yes --skip db.postgres18 --skip tools.vaultPreview what would run without executing:
curl -fsSL "..." | bash -s -- --print-planNote: Using
--no-depsbypasses safety checks and may result in broken installs. Only use if you've already installed dependencies separately.
Custom Post-Install Hooks
Add custom steps by placing scripts in ~/.acfs/hooks/:
mkdir -p ~/.acfs/hooks
cat > ~/.acfs/hooks/post-install.sh << 'EOF'
#!/bin/bash
# Custom post-install steps
echo "Running custom configuration..."
# Your commands here
EOF
chmod +x ~/.acfs/hooks/post-install.shACFS will execute post-install.sh after the main installation completes.
Override Tool Versions
To pin specific tool versions, set environment variables:
export BUN_VERSION="1.1.0"
export UV_VERSION="0.5.0"
# Then run installerNote: Not all tools support version pinning. Check individual tool documentation.
Future Roadmap
ACFS is actively developed. Here's what's coming:
Near-Term (Q1 2025)
- Manifest-driven execution:
install.shconsumes generated module libraries while retaining explicit authored handoffs for registeredgenerated:falsemodules ✓ - Tailscale integration: Zero-config VPN for secure remote access ✓
- Services setup wizard: Guide users through service account setup (
acfs services-setup) ✓ - Interactive module selection: Choose what to install via TUI
Mid-Term (Q2 2025)
- ARM64 (aarch64) Linux VPS: of the 29 stack tools, 20 publish native aarch64 Linux builds and 5 ship scripts;
rchcompiles from source;pcr,pfrandjfphave no release the provenance report can inspect. An emulated full arm64 install completes, its only smoke failures caused by the emulator's missing setuid support (not yet run on native ARM hardware) ✓ - Apple Silicon (macOS) hosts
- Offline mode: Pre-downloaded package bundles
- Team mode: Shared configurations across team members
- Plugin system: Third-party tool integrations
Long-Term (2025+)
- ACFS Cloud: Managed VPS provisioning + ACFS install in one click
- IDE integrations: VSCode/Cursor extensions for remote ACFS management
- Agent marketplace: Pre-configured agent personalities and workflows
- Enterprise features: SSO, audit logging, compliance
Performance Benchmarks
Installation times vary by VPS provider and network conditions. Here are typical benchmarks:
Installation Time by Phase
| Phase | Typical Duration | Notes |
|---|---|---|
| User Setup | 10-15s | Fast, mostly checks |
| Filesystem | 5-10s | Creating directories |
| Shell Setup | 2-4 min | Oh-My-Zsh clone is slow |
| CLI Tools | 3-5 min | Many apt packages |
| Languages | 3-5 min | Rust compile takes longest |
| Agents | 1-2 min | Fast bun installs |
| Cloud | 1-2 min | Fast bun installs |
| Stack | 4-6 min | Cargo installs |
| Finalize | 30-60s | Config deployment |
| Total | 15-25 min | Typical full install |
Factors Affecting Speed
| Factor | Impact | Optimization |
|---|---|---|
| Network latency | High | Choose VPS close to package mirrors |
| Disk I/O | Medium | SSD/NVMe preferred |
| CPU cores | Medium | More cores = faster compilation |
| RAM | Low | 4GB is sufficient |
| Provider | Variable | OVH and Contabo offer excellent value |
Resume Performance
Resuming from checkpoint is fast because completed phases are skipped:
Full install: 20 minutes
Resume from 50%: 10 minutes
Resume from 90%: 2 minutesLicense
MIT License (with OpenAI/Anthropic Rider). See LICENSE for details.
Links
- Website: agent-flywheel.com — Interactive wizard for beginners
- GitHub: Dicklesworthstone/agentic_coding_flywheel_setup
- Related Projects:
- ntm - Named Tmux Manager
- beads_viewer - Task management TUI
- mcp_agent_mail_rust - Agent coordination
- cass - Agent session search
- dcg - Destructive Command Guard
- ru - Repo Updater
About Contributions
Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via gh and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.