Skip to content

Archon Directories

This document explains the Archon directory structure and configuration system for developers contributing to or extending Archon.

Archon provides a unified directory and configuration system with:

  1. Consistent paths across all platforms (Mac, Linux, Windows, Docker)
  2. Configuration precedence chain (env > global > repo > defaults)
  3. Workflow engine integration with YAML definitions in .archon/workflows/
~/.archon/ # ARCHON_HOME
├── workspaces/ # Per-project storage (project-centric layout)
│ ├── <owner>/<repo>/ # a registered repo with a remote
│ ├── _local/<basename>/ # a no-remote local git repo
│ ├── _folder/<slug>/ # a folder project (non-git; runs in place)
│ └── _cwd/<basename>/ # an unregistered working directory
│ ├── source/ # Clone or symlink -> local path (repo kinds only)
│ ├── worktrees/ # Git worktrees for this project (repo kinds only)
│ ├── artifacts/ # Workflow artifacts — NEVER in git
│ │ ├── runs/<run-id>/ # $ARTIFACTS_DIR for one run
│ │ │ └── nodes/ # typed output sidecars (<id>.md + <id>.meta.json)
│ │ ├── scopes/<workflow>/<scope>/ # cross-invocation artifacts (persist_session)
│ │ └── uploads/<conv-id>/ # Web UI file uploads (ephemeral)
│ ├── logs/<run-id>.jsonl # Workflow execution logs
│ └── state/ # $STATE_DIR — cross-run state, shared per project
├── workflows/ commands/ scripts/ # Home-scoped ("global") definitions
├── worktrees/ # Legacy global worktrees (repos not in workspaces/)
├── vendor/codex/ # Codex native binary (binary builds, user-placed)
├── web-dist/<version>/ # Cached web UI dist (archon serve, binary only)
├── update-check.json # Update check cache (binary builds only, 24h TTL)
├── tier-notice.json # One-time tier-default notice state (CLI, per version)
├── credential-key # Auto-provisioned per-user credential encryption key
├── archon.db # SQLite database (when DATABASE_URL is unset)
└── config.yaml # Global user configuration

Purpose:

  • workspaces/<project>/ - Everything one project produces. The project segment is resolved once per run from the codebase identity: owner/repo for a repo with a remote, _local/<basename> for a no-remote local repo, _folder/<slug> for a folder project, and _cwd/<basename> when a run has no registered codebase at all. Folder projects and _cwd projects have no source/ or worktrees/ — they run in place.
  • workspaces/<project>/artifacts/ - Run output. $ARTIFACTS_DIR is artifacts/runs/<run-id>/.
  • workspaces/<project>/logs/ - One JSONL execution log per run.
  • workspaces/<project>/state/ - $STATE_DIR. Cross-run workflow state, shared by every workflow in the project. Survives worktree teardown; never visible to git.
  • worktrees/ - Legacy fallback for repos not registered under workspaces/
  • config.yaml - Non-secret user preferences

Each run also records the project root it resolved in workflow_runs.output_root, so an old run’s artifacts stay addressable even if the codebase is later renamed.

any-repo/.archon/
├── commands/ # Custom commands
│ ├── plan.md
│ └── execute.md
├── workflows/ # Workflow definitions (YAML files)
│ └── pr-review.yaml
├── scripts/ # Named scripts for script: nodes (.ts/.js for bun, .py for uv)
└── config.yaml # Repo-specific configuration

Purpose:

  • commands/ - Slash commands (auto-loaded on clone)
  • workflows/ - YAML workflow definitions in flat, one-level grouped, or exact <pack>/<workflow>/ packaged layouts
  • scripts/ - Named scripts referenced by script: nodes
  • config.yaml - Project-specific settings

The repo directory holds source only. Everything a run produces lives under ~/.archon/workspaces/<project>/.

.archon/state/ was a prompt-level convention with no engine support — workflows did mkdir -p .archon/state relative to cwd. It had two problems: inside an isolated run that path is the worktree, so the “cross-run memory” was destroyed at cleanup; and Archon never writes a .gitignore, so in a user’s repository the directory was fully stageable.

It is replaced by $STATE_DIR. If Archon finds a legacy directory when a run starts it logs one warning with the exact move command and moves nothing:

Terminal window
mv <repo>/.archon/state/* ~/.archon/workspaces/<project>/state/

Then replace .archon/state/ with $STATE_DIR/ in the workflow’s prompts and scripts, and delete any mkdir -p .archon/state — the executor pre-creates $STATE_DIR.

In Docker containers, the Archon home is fixed at /.archon/ (root level). This is:

  • Mounted as a named volume for persistence
  • Not overridable by end users (simplifies container setup)

All path resolution is centralized in packages/paths/src/archon-paths.ts (@archon/paths).

// Get the Archon home directory
getArchonHome(): string
// Returns: ~/.archon (local) or /.archon (Docker)
// Get workspaces directory
getArchonWorkspacesPath(): string
// Returns: ${ARCHON_HOME}/workspaces
// Get global worktrees directory (legacy fallback)
getArchonWorktreesPath(): string
// Returns: ${ARCHON_HOME}/worktrees
// Get global config path
getArchonConfigPath(): string
// Returns: ${ARCHON_HOME}/config.yaml
// Get cached web UI distribution directory for a given version
getWebDistDir(version: string): string
// Returns: ${ARCHON_HOME}/web-dist/${version}
// Get command folder search paths (priority order)
getCommandFolderSearchPaths(configuredFolder?: string): string[]
// Returns: ['.archon/commands'] + configuredFolder if specified
function isDocker(): boolean {
return (
process.env.WORKSPACE_PATH === '/workspace' ||
(process.env.HOME === '/root' && Boolean(process.env.WORKSPACE_PATH)) ||
process.env.ARCHON_DOCKER === 'true'
);
}
function isWSL(): boolean {
// Either signal is sufficient:
// - WSL_DISTRO_NAME env var is set (always true inside a WSL distro)
// - /proc/sys/kernel/osrelease contains "microsoft" (lower-cased)
// The /proc read is wrapped in try/catch: on environments without a
// readable /proc (macOS, Windows, restricted sandboxes) it conservatively
// returns false.
}
function getWSLDistroName(): string | undefined {
// Returns the WSL_DISTRO_NAME env var if present, otherwise undefined.
// Only reads the env var — isWSL() may still be true via the /proc
// fallback while this returns undefined.
}

Used to build Windows-host-friendly vscode://vscode-remote/wsl+<distro>/... IDE URIs when Archon runs inside WSL (surfaced as is_wsl / wsl_distro on /api/health).

PlatformgetArchonHome()
macOS/Users/<username>/.archon
Linux/home/<username>/.archon
WindowsC:\Users\<username>\.archon
Docker/.archon

Configuration is resolved in this order (highest to lowest priority):

  1. Environment Variables - Secrets, deployment-specific
  2. Global Config (~/.archon/config.yaml) - User preferences
  3. Repo Config (.archon/config.yaml) - Project-specific
  4. Built-in Defaults - Hardcoded in packages/core/src/config/config-types.ts
// Load merged config for a repo
const config = await loadConfig(repoPath);
// Load just global config
const globalConfig = await loadGlobalConfig();
// Load just repo config
const repoConfig = await loadRepoConfig(repoPath);

Key configuration options:

OptionEnv OverrideDefault
ARCHON_HOMEARCHON_HOME~/.archon
Default AI AssistantDEFAULT_AI_ASSISTANTclaude
Telegram StreamingTELEGRAM_STREAMING_MODEstream
Discord StreamingDISCORD_STREAMING_MODEbatch
Slack StreamingSLACK_STREAMING_MODEbatch

Command detection searches in priority order:

  1. .archon/commands/ - Always searched first
  2. Configured folder from commands.folder in .archon/config.yaml (if specified)

Example configuration:

.archon/config.yaml
commands:
folder: .claude/commands/archon # Additional folder to search

To add a new managed directory:

  1. Add function to packages/paths/src/archon-paths.ts:
export function getArchonNewPath(): string {
return join(getArchonHome(), 'new-directory');
}
  1. Update Docker setup in Dockerfile
  2. Update volume mounts in docker-compose.yml
  3. Add tests in packages/paths/src/archon-paths.test.ts

To add new configuration options:

  1. Add type to packages/core/src/config/config-types.ts:
export interface GlobalConfig {
// ...existing
newFeature?: {
enabled?: boolean;
setting?: string;
};
}
  1. Add default in getDefaults() function
  2. Use via loadConfig() in your code

Why ~/.archon/ instead of ~/.config/archon/?

Section titled “Why ~/.archon/ instead of ~/.config/archon/?”
  • Simpler path (fewer nested directories)
  • Follows Claude Code pattern (~/.claude/)
  • Cross-platform without XDG complexity
  • Easy to find and manage manually
  • Bun has native support (via yaml package)
  • Supports comments (unlike JSON)
  • Workflow definitions use YAML
  • Human-readable and editable
  • Simplifies container setup
  • Predictable volume mounts
  • No user confusion about env vars in containers
  • Matches convention (apps use fixed paths in containers)
  • Mirrors git config pattern (familiar to developers)
  • Secrets stay in env vars (security)
  • User preferences in global config (portable)
  • Project settings in repo config (version-controlled)

The config type system is designed for:

  • Web UI configuration
  • API-driven config updates
  • Real-time config validation