toeverything/AFFiNE
There can be more than Notion and Miro. AFFiNE(pronounced [ə‘fain]) is a next-gen knowledge base that brings planning, sorting and creating all together. Privacy first, open-source, customizable and ready to use.
Mixed signals — read the receipts
- ✓Last commit 1d ago
- ✓5 active contributors
- ✓Other licensed
- ✓CI configured
- ✓Tests present
- ⚠Small team — 5 top contributors
- ⚠Concentrated ownership — top contributor handles 65% of commits
- ⚠Non-standard license (Other) — review terms
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Embed this verdict
[](https://repopilot.app/r/toeverything/affine)Paste into your README — the badge live-updates from the latest cached analysis.
Onboarding doc
Onboarding: toeverything/AFFiNE
Generated by RepoPilot · 2026-05-05 · Source
Verdict
WAIT — Mixed signals — read the receipts
- Last commit 1d ago
- 5 active contributors
- Other licensed
- CI configured
- Tests present
- ⚠ Small team — 5 top contributors
- ⚠ Concentrated ownership — top contributor handles 65% of commits
- ⚠ Non-standard license (Other) — review terms
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
TL;DR
AFFiNE is an open-source, privacy-first all-in-one workspace that merges document editing (like Notion), whiteboard/canvas (like Miro), and database/planning tools into a single app. It uses BlockSuite as its editor engine with CRDT-based real-time collaboration, supports local-first storage via SQLite (managed in packages/frontend/native/nbstore), and runs as both a web app and native desktop/mobile app via Tauri/Swift/Kotlin bindings. Monorepo (Yarn workspaces + Cargo workspace) split across packages/backend (NestJS server, native Node addons), packages/frontend (web app, Tauri desktop, mobile-native iOS/Android), packages/common (y-octo CRDT engine, shared native utils), and packages/infra. .github/actions/ contains reusable CI steps; .docker/ contains selfhost and dev environment configs.
Who it's for
Knowledge workers, developers, and teams who want a self-hostable, open-source alternative to Notion+Miro with full data ownership. Contributors are typically TypeScript/Rust engineers working on editor infrastructure, sync engines, or AI integrations.
Maturity & risk
AFFiNE has a substantial codebase (23M+ lines of TypeScript, 1.2M Rust) with active CI via GitHub Actions (.github/actions/), structured Docker deployment configs (.docker/selfhost/compose.yml), and CLA enforcement (.github/CLA.md). The project is actively developed and used in production at app.affine.pro, though it has a 'beta' feel with rapid feature churn — production-ready for self-hosters willing to track releases.
The monorepo has heavy polyglot complexity (TypeScript, Rust, Swift, Kotlin, C) with custom Rust dependencies pinned to specific git revisions (e.g. docx-parser at toeverything/docx-parser rev 380beea, mermaid-rs-renderer at toeverything/mermaid-rs-renderer), creating risk if those upstream repos become stale. The core sync and storage layer (y-octo, nbstore) is custom-built in Rust, meaning bugs there affect all clients. Self-hosted deployments depend on a PostgreSQL + Redis stack, and misconfigured .docker/selfhost/.env.example settings are a common failure point.
Active areas of work
Active work is visible on: Rust edition 2024 migration (Cargo.toml workspace uses resolver '3'), the nbstore SQLite backend (packages/frontend/native/nbstore as a dedicated Cargo member), mobile native bindings (packages/frontend/mobile-native), and AI/LLM integration via llm_adapter and llm_runtime crates with mermaid rendering via mermaid-rs-renderer.
Get running
git clone https://github.com/toeverything/AFFiNE.git && cd AFFiNE && yarn install && cp .docker/dev/.env.example .docker/dev/.env && cd .docker/dev && cp compose.yml.example compose.yml && docker compose up -d && cd ../.. && yarn dev
Daily commands: yarn dev (web dev server), yarn workspace @affine/server dev (backend only), cargo build --workspace (all Rust crates). For full stack with Docker: cd .docker/dev && docker compose up -d.
Map of the codebase
.github/helm/affine/Chart.yaml— Top-level Helm chart definition orchestrating all AFFiNE service sub-charts (front, graphql, gcloud-sql-proxy, doc) — the root deployment contract every contributor must understand..github/helm/affine/templates/configmap.yaml— Central Kubernetes ConfigMap template that injects runtime environment variables shared across all AFFiNE services in production deployments..github/helm/affine/charts/graphql/templates/deployment.yaml— Deployment manifest for the GraphQL/API server — the core backend service; defines replica strategy, resource limits, and environment wiring..github/deployment/node/Dockerfile— Production Dockerfile for the Node.js server; defines the exact runtime image, build steps, and entry point for all server-side code..devcontainer/devcontainer.json— Dev container specification that defines the canonical local development environment — every new contributor starts here to reproduce the correct toolchain..docker/selfhost/compose.yml— Self-hosted Docker Compose definition exposing all production services (web, sync, db, redis) — the reference architecture for the full stack..cargo/config.toml— Workspace-level Cargo configuration controlling cross-compilation targets and linker flags for all Rust native modules (nbstore, y-octo, SQLite bindings).
How to make changes
Add a new GraphQL API endpoint
- Define the new NestJS resolver/service module under the graphql server package source. Follow the existing module pattern with @Resolver, @Query/@Mutation decorators. (
.github/helm/affine/charts/graphql/templates/deployment.yaml) - If the feature requires new DB columns, create a Prisma migration and ensure it is executed via the migration Job before the new server version rolls out. (
.github/helm/affine/charts/graphql/templates/migration.yaml) - Add any new environment variables (API keys, feature flags) to the shared ConfigMap template so they are available to the GraphQL pod at runtime. (
.github/helm/affine/templates/configmap.yaml) - If new secrets are needed (e.g. a third-party API key), add an entry to the GraphQL secret manifest and reference it in the deployment env section. (
.github/helm/affine/charts/graphql/templates/secret.yaml)
Add a new Rust native module (NAPI binding)
- Create a new crate directory under packages/ and add it to the workspace members array in the root Cargo.toml. Follow the existing pattern from affine_nbstore or affine_common. (
.cargo/config.toml) - Declare shared workspace dependencies your new crate needs in [workspace.dependencies] and reference them with
workspace = truein your crate's Cargo.toml. (.cargo/config.toml) - Update the build-rust CI action to include compilation and artifact upload for the new crate target so NAPI bindings are published on every release. (
.github/actions/build-rust/action.yml)
Deploy a new microservice to the cluster
- Create a new Helm sub-chart directory under .github/helm/affine/charts/ with Chart.yaml, values.yaml, and a templates/ directory mirroring the doc or graphql sub-chart structure. (
.github/helm/affine/charts/doc/Chart.yaml) - Add a deployment.yaml and service.yaml in the new sub-chart templates following the existing pattern (image tag from values, env from configmap, readiness probe). (
.github/helm/affine/charts/doc/values.yaml) - Register the new service in the top-level Ingress template if it needs external traffic, adding the appropriate path rule and backend service reference. (
.github/helm/affine/templates/ingress.yaml) - Add the new service to the self-hosted compose.yml so local and self-hosted users can run it without Kubernetes. (
.docker/selfhost/compose.yml)
Add a new environment variable or feature flag
- Add the variable to the shared ConfigMap template so it is mounted into all pods that need it. Use Helm value references so it can be overridden per environment. (
.github/helm/affine/templates/configmap.yaml) - Document the new variable in the self-hosted environment example file so self-hosted operators know to configure it. (
.docker/selfhost/.env.example) - Add the variable to the Docker Compose dev environment example so local developers can enable/disable the feature flag during development. (
.docker/dev/.env.example)
Why these technologies
- Y.js / y-octo (Rust CRDT) — Enables conflict-free real-time collaborative editing across peers without a central lock; the Rust re-implementation (y-octo) provides 10–100× throughput over the JS original for large documents.
- Next.js (React SSR) — Provides server-side rendering for fast initial loads and SEO, while allowing the rich client-side block editor to hydrate into a fully interactive SPA.
- NestJS + GraphQL — NestJS gives a structured, decorator-driven module system suitable for a large TypeScript backend; GraphQL allows the frontend to fetch precisely the workspace/block data it needs, reducing over-fetching.
- PostgreSQL —
Traps & gotchas
- The .docker/dev/compose.yml is NOT committed — you must copy compose.yml.example manually. 2) Rust crates use edition '2024' and resolver '3', requiring a recent nightly or stable Rust ≥1.85; older toolchains will fail silently on workspace resolution. 3) Several Rust dependencies (docx-parser, mermaid-rs-renderer) are pinned to private/forked git repos at specific revs — if those repos are private or deleted, offline/fresh builds break. 4) Mobile builds (Swift/Kotlin) require Xcode and Android SDK separately; they are not part of the devcontainer setup. 5) The napi-rs native addons must be compiled for your specific Node.js version and OS; prebuilt binaries may not exist for uncommon platforms.
Architecture
Concepts to learn
- CRDT (Conflict-free Replicated Data Type) — AFFiNE's entire real-time collaboration and local-first sync is built on y-octo, a custom Rust CRDT implementation; understanding CRDTs explains why merging offline edits works without conflicts.
- Local-first software — AFFiNE stores data in local SQLite via nbstore so the app works offline and syncs later — this architectural choice drives the nbstore Rust crate and the sync design.
- napi-rs (Node.js native addons via Rust) — AFFiNE's Rust crates (nbstore, native) are exposed to the Node.js/Electron/Tauri frontend via napi-rs bindings — understanding this is required to modify or debug native storage and image processing code.
- Yjs document model — y-octo implements the Yjs CRDT protocol; the shared types (Y.Doc, Y.Map, Y.Array) define AFFiNE's block data structure for collaborative editing.
- Tauri (Rust-based desktop app framework) — AFFiNE's desktop app wraps the web frontend in Tauri rather than Electron, enabling Rust-native OS access and smaller binaries — Tauri-specific APIs appear throughout packages/frontend/native.
- Block-based editor architecture — AFFiNE uses BlockSuite where all content (text, images, databases, whiteboards) are typed blocks in a shared CRDT document — this differs from traditional DOM/contenteditable editors and affects how all editor features are built.
- Self-hosting with reverse proxy (nginx + Docker Compose) — The .docker/selfhost and .docker/dev setups use nginx as a reverse proxy to route HTTP, WebSocket sync, and static asset requests — misconfiguring this is the most common self-hosting failure point.
Related repos
toeverything/blocksuite— The editor/block engine that powers AFFiNE's document and whiteboard editing — a direct companion repo that AFFiNE depends on.toeverything/OctoBase— Predecessor/companion CRDT sync engine that evolved into y-octo used inside this repo for local-first data sync.AppFlowy-IO/AppFlowy— Direct open-source alternative solving the same Notion-replacement problem, also uses Flutter + Rust architecture.outline/outline— Another self-hostable open-source knowledge base/wiki alternative in the same space, Node.js + React based.yjs/yjs— The CRDT library that y-octo is inspired by/compatible with — understanding Yjs is essential for working on AFFiNE's sync layer.
PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add Rust unit tests for affine_nbstore crate (packages/frontend/native/nbstore)
The nbstore crate is a core storage abstraction used by the frontend native layer. Given it is listed as a workspace member and likely handles critical data persistence logic, having comprehensive Rust unit tests would prevent regressions and build contributor confidence. The workspace uses Rust 2024 edition and already has criterion benchmarks as a dev-dependency, suggesting testing infrastructure is partially in place but test coverage for nbstore specifically is likely thin or missing.
- [ ] Audit packages/frontend/native/nbstore/src/ for public functions and structs lacking #[cfg(test)] modules
- [ ] Add unit tests for core storage read/write operations within the nbstore crate using Rust's built-in test framework
- [ ] Add integration tests in packages/frontend/native/nbstore/tests/ covering edge cases like concurrent access (leveraging the async-lock and loom workspace dependencies already declared)
- [ ] Ensure tests run in CI by verifying the existing GitHub Actions workflow at .github/actions/build-rust/action.yml includes
cargo test --package affine_nbstore - [ ] Add a #[test] for schema compatibility between packages/frontend/native/schema and nbstore to catch serialization regressions
Add a GitHub Actions workflow for Rust workspace-wide linting and security audit
The repo has .github/actions/build-rust/action.yml as a reusable action, but there is no evidence of a top-level workflow file that runs cargo clippy, cargo fmt --check, and cargo audit across all workspace members (native, nbstore, schema, sqlite_v1, y-octo, mobile-native, backend/native). Given the workspace has 8 members with complex dependencies including git-pinned crates (docx-parser, mermaid-rs-renderer), a security audit workflow would catch yanked or vulnerable crates early.
- [ ] Create .github/workflows/rust-lint.yml that triggers on pull_request targeting main
- [ ] Add a job that calls .github/actions/build-rust/action.yml for setup, then runs
cargo fmt --check --all - [ ] Add a job step running
cargo clippy --workspace --all-targets --all-features -- -D warningsto enforce lint cleanliness across all 8 workspace members - [ ] Add
cargo auditstep using rustsec/audit-check action to scan Cargo.lock against the RustSec advisory database - [ ] Add a separate job running
cargo test --workspaceto ensure all crates compile and pass tests on every PR - [ ] Reference .cargo/config.toml to ensure any custom target or registry settings are respected in the workflow
Add self-hosting configuration JSON Schema validation and documentation for .docker/selfhost/schema.json
The repo already ships .docker/selfhost/schema.json and .docker/selfhost/config.example.json, indicating a structured self-hosting configuration system. However, there is no documented mapping between the schema fields and the actual environment variables shown in .docker/selfhost/.env.example, and no CI step validates that config.example.json actually conforms to schema.json. This is a high-value gap because self-hosting is a core value proposition of AFFiNE and misconfigured deployments are a top support burden.
- [ ] Read .docker/selfhost/schema.json and .docker/selfhost/config.example.json to identify all defined config keys and their types
- [ ] Add a CI workflow step (e.g. in .github/workflows/) using
ajv-clior Python'sjsonschemato validate config.example.json against schema.json on every PR that touches .docker/selfhost/ - [ ] Cross-reference .docker/selfhost/.env.example with schema.json and document any fields present in one but missing in the other
- [ ] Add a SELF_HOSTING.md or extend .docker/selfhost/README (currently only .docker/dev/README.md exists) docum
Good first issues
- Add missing integration tests for the nbstore Rust crate (packages/frontend/native/nbstore) — the Cargo workspace includes it but there's no visible test infrastructure cited for it in the file list. 2) Document the selfhost schema.json fields in .docker/selfhost/README or a dedicated docs page — the schema.json exists but there's no human-readable explanation of each config option. 3) Add a GitHub Actions workflow lint check using the existing .github/actionlint.yaml config to catch YAML errors in new action files before merge.
Top contributors
- @darkskygit — 51 commits
- @renovate[bot] — 11 commits
- @ibex088 — 8 commits
- @001-mak — 4 commits
- @Abdulrehman-PIAIC80387 — 4 commits
Recent commits
4e169ea— fix(editor): cross browser test stability (#14897) (darkskygit)9e412f5— feat(editor): add collapse/expand functionality to code block component (#14884) (001-mak)5d234ad— fix(editor): single-letter tags in select/multi-select table cell (#14808) (aisharoslan)1ad0883— fix(server): test & schema (darkskygit)74d5eba— fix(editor): stretch latex preview content (#14857) (aisharoslan)a1800cf— feat(editor): remove max-height restriction from mermaid preview container (#14882) (001-mak)fa66139— feat(server): add flag for calendar enable (#14896) (darkskygit)027d163— fix(server): add embedding table repair (#14895) (darkskygit)39abb93— fix(core): prevent Alt+Key shortcuts from hijacking macOS Option-key input (#14866) (Abdulrehman-PIAIC80387)9751cab— fix(editor): native table column resize broken in edgeless mode (#14824) (Abdulrehman-PIAIC80387)
Security observations
- High · Git-pinned Dependencies Without Integrity Verification —
Cargo.toml (workspace dependencies). Two dependencies are pulled directly from GitHub repositories using commit hashes: 'docx-parser' (rev: 380beea) and 'mermaid-rs-renderer'. While commit hashes provide some pinning, these external git dependencies bypass the crate registry's auditing and yanking mechanisms. If the referenced repositories are compromised or the commits are force-pushed/rewritten, malicious code could be introduced without detection via cargo audit or similar tools. Fix: Prefer publishing dependencies to crates.io where they undergo visibility and community review. If git dependencies must be used, vendor them into the repository or use cargo-vet/cargo-crev to audit and attest the specific commits. Add a cargo deny or cargo audit step in CI to flag unvetted git dependencies. - High · Potential Secrets Exposure via .env Example Files —
.docker/dev/.env.example, .docker/selfhost/.env.example. Multiple .env.example files exist at '.docker/dev/.env.example' and '.docker/selfhost/.env.example'. If actual .env files (not just examples) are accidentally committed, or if the example files themselves contain realistic default credentials (e.g., default database passwords, API keys, JWT secrets), they could expose sensitive configuration. The .gitignore files in those directories suggest real .env files are intended to be excluded, but misconfiguration or developer error could still lead to exposure. Fix: Audit the .env.example files to ensure they contain only placeholder values (e.g., 'CHANGE_ME', 'your-secret-here') and never real credentials. Enforce secret scanning in CI (e.g., using git-secrets, trufflehog, or GitHub secret scanning). Confirm .gitignore rules are correctly excluding all real .env files. - High · Dockerfile Sourced from Non-Standard Location —
.github/deployment/node/Dockerfile. The Dockerfile for the node deployment is located at '.github/deployment/node/Dockerfile', which is inside the .github directory. Dockerfiles in non-standard locations may receive less security scrutiny. Additionally, if this Dockerfile uses base images without pinned digests (e.g., 'node:latest' or 'node:20' without a SHA256 digest), it is vulnerable to supply-chain attacks where the upstream image is replaced with a malicious version. Fix: Pin all base images to their specific SHA256 digest (e.g., FROM node:20-alpine@sha256:...). Run container image scanning (e.g., Trivy, Snyk, Docker Scout) in CI. Move Dockerfiles to a more prominent location to ensure regular review. - High · Use of 'libwebp-sys' - Historical Critical CVEs in libwebp —
Cargo.toml (workspace dependencies) - libwebp-sys = 0.14.2. The dependency 'libwebp-sys = 0.14.2' binds to the native libwebp library. libwebp has had several critical vulnerabilities, most notably CVE-2023-4863 (CVSS 9.6), a heap buffer overflow that affected all major browsers and applications. Depending on the bundled libwebp version in the 0.14.2 release of this crate, the application may be exposed to this or related vulnerabilities. Similar concerns apply to the 'image' crate with webp feature enabled. Fix: Verify that libwebp-sys 0.14.2 bundles libwebp >= 1.3.2 (the patched version). Run 'cargo audit' and check for advisories against this crate. Consider updating to the latest version of libwebp-sys. Integrate cargo-audit or cargo-deny into the CI pipeline. - Medium · Self-Hosted Configuration with Potentially Insecure Defaults —
.docker/selfhost/compose.yml, .docker/selfhost/config.example.json. The '.docker/selfhost/config.example.json' and 'schema.json' files define the self-hosted configuration schema. Self-hosted deployments often have weaker default security postures (e.g., HTTP instead of HTTPS, default credentials, open admin interfaces). The compose.yml for self-hosting may expose ports directly to the host without adequate firewall or reverse proxy configuration documented. Fix: Ensure the compose.yml does not expose database or internal service ports to 0.0.0.0. Require HTTPS configuration explicitly. Document security hardening steps prominently in the self-hosting README. Consider providing
LLM-derived; treat as a starting point, not a security audit.
Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.