Self-hosted personal AI agent infrastructure in Rust.
Praxis is built for one operator, long-running local state, and safe autonomous behavior. It is not a hosted assistant and not a generic chatbot shell. The goal is a private, durable agent runtime that can wake up, inspect its own world, learn from execution history, and become more useful over time without losing operator trust.
The reference implementation is Axonix. Praxis is the framework extraction of those patterns into a reusable codebase.
- Private by default: local files, local SQLite, self-hosted runtime.
- Built to last: identity, goals, memory, analytics, and proposals survive across sessions.
- Safety-first autonomy: approvals, path policy checks, loop guards, quality gates, and deterministic offline fallbacks.
- Rust and Docker friendly: single binary, small modules, and container-ready workflows from the start.
Praxis is in active development, but it is already a working local operator runtime.
Shipped today:
- Lightweight
praxis ask ...prompts that do not create or mutate session state - Resumable
orient -> decide -> act -> reflect -> sleepsession loop - Linux/macOS path handling plus Docker-first data-dir support
- Markdown identity and goal files with dependency-aware goal selection
- Parent/child goal metadata so open child work is selected before umbrella parent goals
- SQLite-backed sessions, approvals, memories, reviews, evals, forensics, learning runs, and provider usage
- Context budgeting, anatomy indexing, repeated-read avoidance, and token/cost ledgers
- Anchor-preserving summarization for oversized context sources
- Adaptive context allocation that nudges source budgets based on prior successful sessions
- Hot/cold memory search plus operational memory for do-not-repeat notes and known bugs
- In-place cold-memory decay for stale long-term memories, without blindly demoting them back to hot memory
- Tool registry, approval queue, path policy enforcement, and loop guards for identical or repeating multi-step tool patterns
- File-mutation circuit breakers for excessive protected-file writes or oversized append payloads
- First real tool execution path for approved append-only writes inside allowed Praxis data files
- Auto-maintained
CAPABILITIES.mdnotes for installed tools, recent examples, and failure history - Provider routing with
stub, Claude, OpenAI, Ollama, and router-mode failover - Explicit
budgets.tomllimits for ask/run attempt count, token spend, and estimated cost - Named model profiles in
profiles.tomlsoquality,budget, andofflinemodes can steer backend behavior and context aggressiveness - Opt-in local-first fallback so low-risk
askandactphases can prefer Ollama before cloud providers - Model canary ledger plus
freeze_on_model_regressiongating so remote routes can be frozen until they pass an explicit canary run - Runtime heartbeat file plus
praxis heartbeat checkandscripts/check-heartbeat.shfor external liveness checks - Telegram operator commands and a lightweight SSE/dashboard server
- Boundary maintenance commands plus recurring weekly review prompts surfaced in
statusand/boundaries - Explicit attachment policies for
praxis ask --file ..., with visible reject, chunk, or summarize handling for oversized text files - Reviewer/eval quality gates during Reflect
- Automatic markdown postmortems for reviewer failures, eval regressions, and similar bad outcomes
- Fixture-backed replay tests for stateful session, forensics, and approval flows
- Argus analysis for drift, repeated work, failures, and token hotspots
- Learning runtime that mines opportunities and syncs them into
PROPOSALS.md - Opportunity acceptance that promotes mined work into durable goals in
GOALS.md - Manifest-versioned state export/import plus human-readable audit export
- Optional daily backup snapshots with retention pruning on top of manual export/import
AGENTS.mdpattern capture with CLI support for future-run conventions and gotchas
Not finished yet:
- Broader tool execution beyond the first controlled data-write path
- Full rollout canaries tied into watchdog-driven binary swaps and rollback automation
- Full watchdog/auto-update process management beyond the new heartbeat backstop
- Richer dashboard UI and additional messaging platforms
- Automatic scheduled backup snapshots for long-lived state
- Deeper memory consolidation, reinforcement, and longer-horizon calibration
Initialize a local Praxis data directory:
cargo run -- --data-dir ./local-data init --name "Praxis" --timezone UTCRun a single session:
cargo run -- --data-dir ./local-data run --onceInspect the current state:
cargo run -- --data-dir ./local-data statusValidate the installation:
cargo run -- --data-dir ./local-data doctorBy default Praxis uses the deterministic stub backend, so the basic workflow works fully offline.
praxis init seeds both praxis.toml and providers.toml.
Use a single remote backend:
[agent]
backend = "claude"export ANTHROPIC_API_KEY=...Or enable routed failover:
[agent]
backend = "router"[[providers]]
provider = "claude"
model = "claude-3-5-sonnet-latest"
[[providers]]
provider = "openai"
model = "gpt-5.4-mini"
[[providers]]
provider = "ollama"
model = "llama3.2"
base_url = "http://127.0.0.1:11434"When router mode is active, Praxis records every provider attempt, token count, and estimated cost in SQLite so status and Argus can explain what actually happened.
If you want Praxis to freeze remote model use until a provider/model pair passes an explicit probe, enable:
[agent]
freeze_on_model_regression = trueThen record a canary before normal remote use:
cargo run -- --data-dir ./local-data canary run
cargo run -- --data-dir ./local-data canary statusAsk can also ingest local UTF-8 text attachments with an explicit overflow policy:
cargo run -- --data-dir ./local-data ask \
--file ./notes/brief.md \
--attachment-policy summarize \
"Pull out the action items."Praxis is intended to stay runnable in Docker from the start.
Initialize container state:
docker compose run --rm praxis-initRun one session:
docker compose run --rm praxis-runCheck status:
docker compose run --rm praxis-statusThe compose file binds ./docker-data to /var/lib/praxis, so state persists across container runs.
praxis ask ...praxis run --oncepraxis statuspraxis doctorpraxis boundaries showpraxis forensics latestpraxis argus --limit 10
praxis ask ... is synchronous and stateless. praxis run --once is a real session run that updates durable Praxis state.
praxis queuepraxis approve <id>praxis reject <id>praxis tools listpraxis tools register ...praxis tools request ...
Current real execution slice:
cargo run -- --data-dir ./local-data tools request \
--name praxis-data-write \
--summary "Append reviewed journal note" \
--write-path JOURNAL.md \
--append-text "Operator approved this note."After approval, praxis run --once will execute that request by appending the approved text to the declared allowed file inside the Praxis data directory.
Praxis can ingest notes from learning/sources/, synthesize learnings, detect repeated work, and create a throttled proposal queue:
cargo run -- --data-dir ./local-data learn note "Prefer cargo test --locked before pushing"
cargo run -- --data-dir ./local-data learn run
cargo run -- --data-dir ./local-data learn list
cargo run -- --data-dir ./local-data learn accept 1
cargo run -- --data-dir ./local-data learn dismiss 2LEARNINGS.md is now an append-only operational log. Manual learn note entries and automatic source syntheses both append structured timestamped records instead of rewriting history.
Accepted opportunities are not just status changes. Praxis links them into PROPOSALS.md and promotes them into GOALS.md, so the main loop can pick them up as real work later.
Praxis now has an explicit AGENTS.md surface for project-specific conventions, gotchas, and handoff notes that future sessions should load early:
cargo run -- --data-dir ./local-data agents view
cargo run -- --data-dir ./local-data agents add --section workflow --note "Prefer project-local scripts over one-off shell commands."
cargo run -- --data-dir ./local-data agents add --section gotcha --note "Docker rebuilds are expected to take a while after backend changes."That file is part of the foundation set, is loaded into Orient as its own context source, and is included in portable exports/imports.
Praxis now treats hard limits as a maintained surface instead of a one-time setup note:
cargo run -- --data-dir ./local-data boundaries show
cargo run -- --data-dir ./local-data boundaries add Never deploy after midnight
cargo run -- --data-dir ./local-data boundaries confirm --note "Reviewed after setup"status and Telegram /boundaries will surface when the weekly alignment review is due and explicitly ask whether hard limits changed.
Praxis can now export a portable state bundle, import it into a different data directory, and generate a human-readable audit report:
cargo run -- --data-dir ./local-data export state --output ./praxis-backup
cargo run -- --data-dir ./restored-data import --input ./praxis-backup
cargo run -- --data-dir ./local-data export audit --output ./audit.md --days 30State bundles include a versioned manifest, the SQLite schema version, runtime state, config, tools, goals, evals, learning sources, and the core markdown identity files. Imports re-home the config to the target data directory so restores stay portable across machines and Docker paths.
Telegram support currently includes doctoring, polling, and command routing:
export PRAXIS_TELEGRAM_BOT_TOKEN=...
export PRAXIS_TELEGRAM_ALLOWED_CHAT_IDS=12345,67890
cargo run -- --data-dir ./local-data telegram doctor
cargo run -- --data-dir ./local-data telegram poll-once
cargo run -- --data-dir ./local-data telegram run --cycles 0Messaging semantics match the CLI split:
/ask <prompt>is low-latency and does not create a Praxis session./run <task>executes a real stateful session and bypasses quiet-hours deferral because the operator explicitly requested it.
Run the local dashboard/SSE server:
cargo run -- --data-dir ./local-data serve --host 127.0.0.1 --port 8787src/loop/: session runtime and phase orchestrationsrc/context/: budget engine and context assemblysrc/anatomy.rs: file summaries, token estimates, and repeated-read avoidancesrc/identity/: foundation files, goal parsing, and goal promotionAGENTS.md: future-run conventions, gotchas, and handoff notessrc/memory/: memory loading plus operational memory supportsrc/storage/: SQLite persistence for runtime state and analyticssrc/tools/: registry, policy, approval flow, and loop guardssrc/backend/andsrc/providers/: model/provider execution and routingdocs/agent_runtime.md: runtime boundary, adapter ownership, and replacement plansrc/quality/: reviewer and eval gatessrc/learning/: learning runtime, opportunity mining, and proposal syncsrc/argus/: drift, failure, repeated-work, and token hotspot analysissrc/messaging/andsrc/dashboard/: operator surfacestests/: end-to-end CLI coverage
The codebase follows a small-module style and aims to keep Rust source files under 250 lines.
Run the main verification steps locally:
cargo fmt --all
cargo test --locked
docker build --tag praxis:ci .The project currently targets offline-deterministic tests by default. Live providers and messaging surfaces are wired behind explicit configuration and environment variables.
The canonical design document is PRAXIS_DESIGN.md. It tracks:
- core runtime architecture
- adopted ideas and future enhancements
- build-order status
- what is already implemented versus what is still planned
MIT