# The default branch is canary, the build numbers run backwards, and the plugin system is coming soon

> AFFiNE is an open-source workspace that merges documents, whiteboard and tables into one canvas, written in TypeScript with a Rust component in the same monorepo. Reading the manifest rather than the feature list shows what adopting it involves. Node is pinned to a single major version, installing runs the project's own init through a postinstall hook, one pre-commit hook formats TypeScript, TOML and Rust together, and a Contributor License Agreement check blocks any pull request until every committer has signed.

**toeverything/AFFiNE** — Privacy-first, local-first knowledge base that fuses docs, whiteboard canvas, and tables into one workspace — an open-source alternative to Notion and Miro.

- Repository: https://github.com/toeverything/AFFiNE
- Website: https://affine.pro
- Stars: 72,995 · Forks: 5,308
- Language: TypeScript
- License: not declared
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/toeverything-affine

## The default branch is canary, and the build suffix counts backwards

The branch a clone lands on is `canary`, not a release line, and the three most recent releases show why that naming matters. They are v2026.9.21-canary.912 published on 2026-09-21, v2026.9.23-canary.909 on 2026-09-23, and v2026.9.26-canary.908 on 2026-09-26.

Read those three in date order and the numeric suffix goes down: 912, then 909, then 908. The date in the tag increases while the counter decreases, so the suffix is not a version ordering.

That has a concrete consequence for anything automated. A script that resolves the newest canary by taking the highest number in the tag list selects canary.912, which is the oldest of the three. Ordering has to come from the date component, and a tool that assumes otherwise will silently install a build from five days ago and call it the latest.

Combined with the branch name, the default experience of this repository is a moving target. A clone is the canary line, the latest-release link resolves to a canary tag, and the repository metadata reports no archived state with a last push on 2026-09-26. Anyone who wants a fixed point has to record a specific tag or commit, and check the date rather than the counter when they do.

## The plugin system is the stated advantage over Notion, and it is coming soon

The README argues its case by naming what it takes from other products. It credits Quip and Notion for the idea that everything is a block, Trello for Kanban, Airtable and Miro for programmable datasheets, Miro and Whimiscal for the edgeless whiteboard, and Remote and Capacities for object-based tags. Then it makes the competitive claim: those products, it says, are not open source and do not have a plugin system like VS Code for contributors to customize.

The self-hosting section carries the other half. It says you have the freedom to manage, self-host, fork and build your own AFFiNE, and then adds that the plugin community and third-party blocks are coming soon, pointing at BlockSuite for more traction.

So today the only half of that claim that exists is the licence. The extension mechanism, which is the part the comparison actually turns on, is announced without a date. Any team whose plan depends on building a custom block, or on a community of shared blocks, is planning against something that is not in the repository yet, and the README gives no version in which it will be.

The document and canvas merge is the part that is real now. Docs and whiteboard are described as fully merged, with any building block placeable on an edgeless canvas, which is a genuine difference from a docs tool with a separate whiteboard tab.

## Node is pinned to one major version, and 23 and later are excluded

The manifest states its runtime requirement as node >=22.12.0 <23.0.0. That is a floor and a ceiling in one line, and the ceiling is the part that catches people out. Node 23, 24 and anything after it fall outside the range.

The repository also carries a `.nvmrc`, so the intended version is recorded for contributors, and a `.codesandbox` directory, which pins a runtime for the browser sandbox. Both are conveniences for working on the code rather than a guarantee for running it.

The consequence is a narrow window for a project whose main pitch is that you host it yourself. A team standardising on a current Node LTS will find the supported range behind their chosen version, and the usual resolution, which is to install with engine enforcement relaxed, means the declared support statement no longer describes what you are running. Because the project ships canary builds on a `canary` branch rather than long-lived release tags, there is also no older version with a wider range to fall back to, so an unsupported runtime is not fixed by downgrading AFFiNE.

This is the kind of constraint that is cheap to satisfy and expensive to discover late, which is why it belongs in the first hour of an evaluation rather than in the first incident.

## postinstall runs the project's own init and installs git hooks

The install step is not only a dependency resolution. The manifest defines a postinstall script that is `yarn affine init && yarn husky`, which means installing the tree executes a TypeScript entry point from the project and then installs Husky's git hooks into the working copy.

That is unremarkable inside a development clone and worth naming anyway, because it is what the script does everywhere else. Anyone who installs this monorepo as a dependency, or restores a CI cache by running an install, executes project code as part of that step. A security review that treats an install as data-only will not expect it.

Every other script delegates the same way. `dev` and `build` are `yarn affine dev` and `yarn affine build`, both routing through a file called `affine.ts` rather than doing work inline, with `r` bound to the same entry point as `affine`:

```
yarn affine dev
yarn affine build
```

So there is one dispatch point for the whole toolchain, and it is a script rather than a set of composed package scripts. That is convenient for a monorepo this size and it means the build logic is somewhere you have to read before you can reason about what `build` does, because the manifest does not show it.

The manifest also carries a `dependenciesMeta` block marking some packages as built and others as not, including Prisma's client and engines, so install-time work is selected per dependency rather than globally.

## One pre-commit hook formats TypeScript, TOML and Rust

The `lint-staged` configuration lists four patterns and three languages. Everything matches `*` and is handed to oxfmt. TypeScript and JavaScript files are additionally handed to oxlint. `*.toml` goes to `taplo format`, and `*.rs` goes to `cargo fmt --`.

That last entry is the informative one. A Rust formatter running inside the pre-commit path of a TypeScript monorepo is not a stray leftover, and the tree supports the reading: there is a `Cargo.toml`, a `Cargo.lock`, a `rust-toolchain.toml`, a `rustfmt.toml`, a `deny.toml` and a `.cargo` directory, and a `blocksuite` workspace entry alongside the JavaScript ones. The project is genuinely polyglot, with the Rust side held in the same repository and the same commit stream.

The consequence for a contributor is a small surprise. A commit that touches only JavaScript can still be modified by a Rust formatter if a `.rs` file happens to match a staged pattern, and a commit from someone who has not installed a Rust toolchain will fail the hook rather than skip it, because `cargo fmt` has to exist to run. The lint scripts are equally strict, with oxlint invoked as `oxlint --deny-warnings`, so warnings are failures rather than noise.

Formatting is also wired into the installed state rather than left to a separate command, which is why the hook and the lint script cannot drift apart.

## A Contributor License Agreement check gates every pull request

One line in the contributing section is a hard gate rather than guidance. It asks you to sign the project's Contributor License Agreement before starting, notes that it takes less than a minute with a GitHub account, and states that pull requests cannot be merged until every committer has signed, naming the `license/cla` check on the pull request.

For an outside contributor that is a legal step before code review begins, and it is enforced by automation rather than by a maintainer remembering. For a company it is the point at which someone with authority has to agree to terms, which is why it belongs in an internal approval process rather than in a contributor's onboarding checklist. The failure mode is a pull request that looks ready and sits unmerged, and the visible symptom is a failing check rather than a message explaining what is needed.

The licensing picture has three parts and they answer different questions. The manifest declares MIT for the package, the repository carries both a `LICENSE` and a `LICENSE-MIT` file, and the repository metadata does not resolve a single licence name. The outbound licence and the inbound contribution terms are separate instruments, and a team assessing this needs both.

The rest of the contribution route is conventional: issue templates for bug reports and feature requests, a contributing document under `docs`, and a separate document listing the types of contributions accepted.

## BlockSuite versions with AFFiNE, and self-hosting is deferred to another site

The editor is not a separate dependency with its own release line. `blocksuite` is a workspace in the root manifest, matching `blocksuite/**/*` alongside the package globs, and it has its own directory and its own site at blocksuite.io. The README defers to it for traction and for self-hosting information.

The consequence is visible to anyone who wants the editor without the workspace. Taking BlockSuite as a library means taking it from a monorepo whose releases are AFFiNE canary tags, so the editor has no independent version cadence that you can observe from here. A change in the editor's data model or block API arrives on AFFiNE's schedule, and there is no editor-only release to pin against a stable build.

Self-hosting has a similar shape. The README gives no commands for it. There is no install snippet, no compose file and no environment variable list in the document, and the self-hosting instructions live at a documentation site rather than in the repository. The repository does hold a `.docker` directory and a `render.yaml` with a `.render` directory beside it, which indicate a container and a Render deployment, but the configuration itself is not in the README.

For a project whose strongest argument is that you can host it yourself, that is the first thing to go and verify, and the place to verify it is the documentation the README links, not the README.

## Conclusion

AFFiNE suits a team that wants a self-hosted, open-source workspace where documents and whiteboard are the same surface rather than two tools, and that is prepared to run canary builds. It does not suit a team that needs a stable release to pin, since the default branch is canary and the numeric build suffix is not ordered by date, and it does not suit a plan built around extending the editor, because the plugin community and third-party blocks are described as coming soon. Before you commit, check three things: whether your Node version falls inside the engines range, which is narrower than you would expect; where you will host it, since the README gives no self-host commands and defers to its own documentation; and whether you will depend on BlockSuite directly, because it lives in this monorepo and versions with AFFiNE.

## FAQ

### Which is better, affine or Notion?

The README calls AFFiNE a better alternative to Notion and Miro, and names what it takes from each: the everything-is-a-block idea from Quip and Notion, Kanban from Trello, programmable datasheets from Airtable and Miro, the edgeless whiteboard from Miro and Whimiscal, and object-based tags from Remote and Capacities. The differences it claims for itself are being open source, being self-hostable, and having a plugin system like VS Code, but the last of those is described as coming soon, so today the difference is the licence and the merged docs and canvas surface.

### how to use affine

It is a workspace in which documents, whiteboard and tables share one surface, described as a true canvas for blocks in any form, where docs and whiteboard are fully merged and any building block can be placed on an edgeless canvas. Data is local-first, so your documents live on your own disk, with real-time sync and collaboration on web and cross-platform clients layered on top when you want it.

### affine how to use ai

The README describes a multimodal assistant that works from a single prompt: turning an outline into slides, summarising an article into a mindmap, sorting a job plan and backlog, writing a work report, and drawing or coding prototype apps and web pages. A separate canvas capability is described as generating a mindmap for brainstorming. The README does not say which models back these, what they cost, or whether they need a separate account.

### how to install affine

The README contains no install commands. It points to a download page on the project site, a live demo, the documentation site, and a dedicated self-hosting page under the documentation, and it links BlockSuite's own site for that. The repository does hold a .docker directory and a render.yaml, so a container and a Render deployment exist, but neither is configured in the README and the build instructions are not in the manifest.

## Sources

- [Official documentation](https://affine.pro)
- [Official README](https://github.com/toeverything/AFFiNE#readme)
- [Project repository](https://github.com/toeverything/AFFiNE)
- [Release notes](https://github.com/toeverything/AFFiNE/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/toeverything-affine
