Contributions welcome. Read RUST_STYLE_GUIDE.md before writing any code, and AGENTS.md for the full implementation guide.
- Rust 1.85+ with
rustfmtandclippy - protoc (protobuf compiler)
- bun (for frontend/interface work)
- just (
brew install justorcargo install just --locked)
Optional: Nix flakes for isolated dev environments (nix develop gives you everything).
- Fork the repo and create a feature branch
- Run
./scripts/install-git-hooks.sh(installs a pre-commit hook that runscargo fmt) cargo buildto verify the backend compiles- For frontend work:
cd interface && bun install - Make your changes
- Run
just preflight && just gate-pr - Submit a PR
Every PR must pass just gate-pr before merge. This mirrors CI and checks:
- Migration safety — new migrations only, never edit existing ones
- Formatting —
cargo fmt --all -- --check - Compile —
cargo check --all-targets - Lints —
cargo clippy --all-targets -Dwarnings - Tests —
cargo test --lib - Integration compile —
cargo test --tests --no-run
Use just gate-pr --fast to skip clippy and integration compile during iteration.
The frontend CI (interface-ci.yml) runs bun ci and bunx tsc --noEmit on interface changes.
Single binary crate (no workspace). Key directories:
src/
├── main.rs — CLI entry, config, startup
├── lib.rs — re-exports
├── config.rs — config loading/validation
├── error.rs — top-level Error enum
├── llm/ — LlmManager, model routing, providers
├── agent/ — Channel, Branch, Worker, Compactor, Cortex
├── hooks/ — SpacebotHook, CortexHook
├── tools/ — reply, branch, spawn_worker, memory_*, etc.
├── memory/ — MemoryStore, hybrid search, graph ops
├── messaging/ — Discord, Telegram, Slack, webhook adapters
├── conversation/ — history persistence, context assembly
├── cron/ — scheduler, CRUD
├── identity/ — SOUL.md, IDENTITY.md, USER.md loading
├── secrets/ — encrypted credentials (AES-256-GCM)
├── settings/ — key-value settings
└── db/ — SQLite migrations, connection setup
interface/ — Dashboard UI (Vite + React + TypeScript)
prompts/ — LLM prompts as markdown (not Rust strings)
docs/ — Documentation site (MDX)
desktop/ — Tauri desktop app
scripts/ — Dev tooling (hooks, gates, builds)
Module roots use src/module.rs, not src/module/mod.rs.
The full guide is in RUST_STYLE_GUIDE.md. Key points:
Imports — three tiers separated by blank lines: (1) crate-local, (2) external crates, (3) std.
Error handling — domain errors per module, wrapped by top-level Error enum via #[from]. Use ? and .context(). Never silently discard with let _ =.
Async — native RPITIT for async traits (not #[async_trait]). tokio::spawn for concurrent work. Clone before moving into async blocks.
Logging — tracing crate, never println!. Structured key-value fields. #[tracing::instrument] for spans.
Lints (enforced in Cargo.toml): dbg_macro = "forbid", todo = "forbid", unimplemented = "forbid".
Testing — #[cfg(test)] at end of file. #[tokio::test] for async. .unwrap() is fine in tests only.
Use bun exclusively — never npm, pnpm, or yarn.
cd interface
bun install # install deps
bun run dev # dev server
bun run build # production buildThe dashboard uses @spacedrive/* packages published to npm from the spaceui monorepo:
@spacedrive/primitives— base UI components@spacedrive/ai— AI chat components@spacedrive/forms— form components@spacedrive/explorer— file explorer components@spacedrive/tokens— design tokens
package.json points to npm versions (e.g. "^0.2.0"). CI pulls from the registry. For local development, bun link overrides them with your local copies.
Local SpaceUI development:
Clone the spaceui repo adjacent to this one, then run the link command:
git clone https://github.com/spacedriveapp/spaceui ../spaceui
just spaceui-linkThis builds SpaceUI, registers all packages as global links, and connects them to interface/. Use bun run watch in the SpaceUI repo for automatic rebuilds.
To unlink and restore npm versions: just spaceui-unlink.
just preflight # validate git/remote state
just gate-pr # full PR gate (mirrors CI)
just gate-pr --fast # skip clippy + integration compile
just typegen # generate TypeScript API types
just check-typegen # verify types match
just build-opencode-embed # build OpenCode embed bundle
just bundle-sidecar # build Tauri sidecar
just desktop-dev # run desktop app in dev mode
just update-frontend-hash # update Nix hash after frontend dep changesSQLite migrations are immutable. Never edit an existing migration file. Always create a new timestamped migration for schema changes.
See ARCHITECTURE.md for the full design. The short version: five process types, each with one job.
- Channels — user-facing LLM, stays responsive, never blocks on work
- Branches — fork channel context to think, return conclusion, get deleted
- Workers — independent task execution with focused tools, no conversation context
- Compactor — programmatic context monitor, triggers compaction before channels fill up
- Cortex — system observer, generates memory bulletins, supervises processes
Key rule: never block the channel. Branch to think, spawn workers to act.
Releases are triggered by git tags (v*). The CI workflow:
- Verifies
Cargo.tomlversion matches the tag - Builds multi-platform binaries (x86_64/aarch64, Linux/macOS)
- Builds Docker images (amd64/arm64)
- Creates a GitHub release with binaries
- Updates the Homebrew tap
FSL-1.1-ALv2 (Functional Source License), converting to Apache 2.0 after two years.