Contributing to Axon

July 31, 2026 · View on GitHub

Last Modified: 2026-07-07

This is the entry point for local development conventions: build setup, the monolith policy, and security guardrails. For running tests see testing.md; for cutting a release see release-checklist.md; for adding a new extension point (source adapter, parser, provider, vector store, REST route, MCP action) see the adding-*.md guides in this directory.


Build setup

This repo follows the build conventions of the rmcp server family. The canonical upstream reference is rmcp-template/docs/contributing/rust.md.

System prerequisites

  • Rust stable ≥ 1.86 (rustup update stable)
  • clang and mold for fast Linux builds: apt install clang mold
  • mingw-w64 for Windows cross-compilation: apt install mingw-w64
  • just command runner (optional): cargo install just

Global Cargo config

Build performance depends on ~/.cargo/config.toml on the developer's machine. See rmcp-template/docs/contributing/rust.md for the expected config (mold linker, dev profile tuning). Compile caching is provided by the mise-managed kache, configured as the global rustc-wrapper. Use KACHE_DISABLED=1 or RUSTC_WRAPPER="" to bypass it for diagnostics.

Local .cargo/config.toml

This repo's .cargo/config.toml contains the xtask alias and the Windows cross-compile linker:

[alias]
xtask = "run --package xtask --"

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"

The Windows linker entry is a per-repo setting because CI environments may not have the standard global ~/.cargo/config.toml. All other settings (profile tuning, mold linker for Linux) are inherited from the global config.

Windows cross-compilation

axon publishes Windows binaries via the release CI workflow. To cross-compile locally:

rustup target add x86_64-pc-windows-gnu
cargo build --target x86_64-pc-windows-gnu --release

Monolith policy

This repository enforces a ratcheting policy to prevent new monolithic files and functions from being introduced.

Scope:

  • Enforced on changed code, not the full codebase.
  • Enforced locally via lefthook pre-commit.
  • Enforced in CI for pull requests and pushes.

Limits:

  • File size limit: 500 lines
  • Rust function size limit: warn at 80 lines, hard fail at 120 lines

Checked file types: file-size enforcement applies only to changed Rust source files (.rs); function-size enforcement applies to changed Rust functions in .rs files.

Test exemptions (the following paths/patterns are exempt):

  • tests/**
  • **/tests/**
  • **/*_test.*
  • **/*.test.*
  • **/*.spec.*
  • benches/**
  • config/**
  • **/config/**
  • **/config.rs

Exceptions: temporary file-level exceptions can be added to .monolith-allowlist (one repo-relative path per line). Use exceptions only when necessary, add a comment with ticket/date/owner above the entry, and remove entries as soon as refactoring is complete.

Local enforcement:

./scripts/install-git-hooks.sh                                    # install hooks once
python3 ~/.claude/hooks/enforce_monoliths.py --staged              # run manually against staged changes

CI enforcement:

python3 ~/.claude/hooks/enforce_monoliths.py --base "$BASE_SHA" --head "$HEAD_SHA"

This keeps enforcement ratcheted to the change set under review.

Config files: policy logic lives in ~/.claude/hooks/enforce_monoliths.py, local hooks in lefthook.yml, the hook installer in scripts/install-git-hooks.sh, the CI job in .github/workflows/ci.yml, and the exception list in .monolith-allowlist.

Security guardrails

Safety and security patterns enforced across the Axon stack.

Credential management

  • All credentials live in ~/.axon/.env with chmod 600 permissions.
  • Never commit real .env files.
  • Use .env.example as a tracked template with placeholder values only.
  • Direct Docker Compose commands should pass --env-file ~/.axon/.env so the canonical env file is used for ${VAR} interpolation; service containers also read ${AXON_HOME:-${HOME}/.axon}/.env.

.gitignore and .dockerignore must include:

.env
*.secret
*.pem
*.key

Lefthook hooks verify security invariants:

HookPurpose
cargo xtask check-env-stagedBlocks commits that include .env files
cargo xtask check-mcp-httpVerifies MCP transport configuration parity

Web panel token model

The current web panel uses the file-backed panel password generated by axon serve, plus the MCP HTTP auth boundary for MCP and first-party action routes.

TokenScopeBrowser-visible
~/.axon/panel-passwordGates /api/panel/config, /api/panel/ops, and setup routesReturned only after /api/panel/login succeeds
AXON_MCP_HTTP_TOKENStatic bearer for /mcp and protected /v1 routesClient-configured secret
OAuth JWT (AXON_MCP_AUTH_MODE=oauth)OAuth bearer for /mcp and protected /v1 routesClient obtains through OAuth flow

Rules:

  • ~/.axon/panel-password must stay mode 0600.
  • Non-loopback HTTP binds require bearer or OAuth auth.
  • Do not expose Chrome, Qdrant, or TEI directly to a network.
  • Mobile clients must require an explicit panel-token unlock for /api/panel/*. Do not fall back to AXON_MCP_HTTP_TOKEN or OAuth tokens for panel config routes on the client.
  • Mobile settings UIs must not write a masked placeholder back to disk as a secret value. Preserve the raw server text privately and patch only genuinely changed secret fields.

MCP OAuth is an HTTP auth mode for MCP and first-party action clients. It does not replace the file-backed panel password used by setup/config panel routes.

Docker security

Non-root execution. The axon container runs the unified server as UID/GID 1000:1000 via Compose and the runtime image USER directive:

user: "1000:1000"

No baked environment. Docker images must not contain credentials at build time: no ENV AXON_PG_URL=... in Dockerfiles, no COPY .env in Dockerfiles. Credentials are injected at runtime via env_file: or container environment:.

Image verification:

# Check for baked secrets
docker inspect axon:local | jq '.[0].Config.Env'

# Check container revision matches git SHA
docker compose --env-file ~/.axon/.env -f docker-compose.prod.yaml ps

Network security

  • All service URLs should use https:// in production.
  • HTTP is acceptable for local development and Docker-internal networking.
  • The Chrome CDP endpoint is HTTP-only by design (internal network).
  • Android allows cleartext only for the configured private Tailscale domains in apps/android/app/src/main/res/xml/network_security_config.xml; any other persisted Android server URL should be https://.

validate_url() (SSRF guard) enforces: no private/loopback IPs, no file:// or other non-HTTP schemes, and connect-time DNS-rebinding is closed by SsrfBlockingResolver (re-checks every resolved IP).

The Spider firewall feature (which would block known malware/phishing domains) is not enabled — spider_firewall's build.rs fails under CI rate-limiting. validate_url() is the primary SSRF guard. See docs/reference/spider-feature-flags.md.

Input handling

  • Default crawl --max-pages is bounded at 2000; use path budgets or explicit --max-pages 0 only for intentional deep crawls.
  • AXON_MAX_PENDING_CRAWL_JOBS caps the queue to prevent runaway crawls.
  • Auto path-prefix scoping limits crawl scope on deep URLs.
  • --respect-robots defaults to false — legal implications acknowledged.
  • chunk_text() splits at 2000 chars with 200-char overlap. Very long pages produce many Qdrant points — monitor collection size after large crawls.

Logging

  • Never log credentials, tokens, or API keys.
  • CLI outputs JSON data to stdout and progress/logs to stderr.
  • Log rotation: 10 MB max, 3 backups (AXON_LOG_MAX_BYTES, AXON_LOG_MAX_FILES).
  • Keep terminal/progress logs on stderr and machine-readable command data on stdout.