Repository instructions for AI coding agents. Keep this file short, concrete, and tied to commands that actually work in this repo. If a fact here conflicts with source files or CI config, trust the source file and update this guide.
Memos is a self-hosted note-taking app.
- Backend: Go 1.27.2, net/http, Connect RPC, gRPC-Gateway, Protocol Buffers.
- Frontend: React 19, TypeScript 7, Vite 8, Tailwind CSS v4, React Query v5.
- Storage: SQLite, MySQL, PostgreSQL.
- Generated API outputs:
proto/gen/for Go/OpenAPI,web/src/types/proto/for TypeScript.
- Read relevant code before editing; prefer local patterns over new abstractions.
- Keep diffs scoped. Do not do repo-wide cleanup, dependency churn, or generated-file rewrites unless the task requires it.
- Do not hand-edit generated proto outputs. Change
.protofiles, then runbuf generate. - Add migrations for all database drivers when schema changes, and update each driver's
LATEST.sql. - Add public API endpoints to
server/api/acl_config.go. - Ask before adding heavy dependencies, changing auth/token behavior, or altering Docker/release workflows.
- ADRs and their companion glossary have been retired. Do not recreate them unless explicitly requested.
- Design documents in
docs/design/stay editable until the feature ships in a tagged release; keep a merged-but-unreleased design in step with its implementation. After release the body is frozen: record later design changes in a dated## Amendmentssection at the end, never by rewriting the body. Seedocs/design/README.md. Verify current behavior in source code and tests, not in design documents. - Brand copy (tagline, descriptions, README intro) comes verbatim from
BRAND.mdinusememos/.github. Change it there first; do not keep brand guidelines in this repo. - Keep contributor guidance in this file and package-local
README.mdordoc.gofiles current when relevant code changes.
Run from the repository root unless a command starts with cd.
# Backend
go run -buildvcs=true ./cmd/memos --port 8081 # Start backend with commit-derived CalVer
go test ./... # Run all Go tests
go test -v ./store/... # Store tests, including DB drivers via TestContainers
go test -v -race ./server/... # Server tests with race detector
go test -v -race ./internal/... # Internal package tests with race detector
go test -v -run TestFoo ./core/... # Run matching Go tests in one tree
go mod tidy -go=1.27.2 # Match CI tidy check
go fix ./... # Apply Go modernization fixes
go fix -diff ./... # Match CI modernization check
golangci-lint run # Go lint, config: .golangci.yaml (includes depguard layering rules)
golangci-lint run --fix # Auto-fix lint, including goimports
# Frontend
cd web && pnpm install # Install dependencies
cd web && pnpm dev # Dev server on :3001, proxying API to :8081
cd web && pnpm lint # Type check + Biome lint
cd web && pnpm test # Vitest unit tests
cd web && pnpm build # Production build
cd web && pnpm release # Build SPA into server/frontend/dist
# Protocol Buffers
cd proto && buf generate # Regenerate Go + TypeScript + OpenAPI
cd proto && buf lint # Lint proto files
cd proto && buf format -w # Format proto filesEvery root folder is named for what it holds. Read the folder's doc.go or README.md before adding to it.
| Path | Holds | May import |
|---|---|---|
cmd/memos/ |
Cobra/Viper CLI setup and server startup | anything |
server/ |
The HTTP process: net/http bootstrap, graceful shutdown, every transport | core, store, provider, markdown, filter, internal |
server/api/ |
Connect/gRPC-Gateway services, ACL config, SSE hub. Thin handlers; rules go to core/ |
|
server/fileserver/ |
Native HTTP file serving, thumbnails, range requests | |
server/frontend/ |
Static SPA serving; dist/ is the built SPA baked in by go:embed |
|
server/mcp/ |
Model Context Protocol server | |
server/auth/ |
JWT access tokens, refresh tokens, PAT handling | |
core/ |
Business rules with no HTTP and no SQL: access, notification, memopayload, memoexport, usagelimit |
store, provider, markdown, filter, internal |
store/ |
Store facade, cache, migrations, Driver interface; store/db/{sqlite,mysql,postgres}/ implement it |
provider, markdown, filter, internal |
markdown/ |
Markdown engine: parser, AST, memos syntax extensions, renderer, memo payload | proto/gen, internal |
filter/ |
CEL filter compiler: parse to IR, render to SQL per driver, filterable field schema | internal |
provider/ |
Backends configured by instance settings: ai, idp, storage |
proto/gen, internal |
internal/ |
Private plumbing with no memos vocabulary (identifier, email, webhook, ratelimit, …) |
third party only |
proto/api/, proto/store/ |
Public API and internal storage proto sources; proto/gen/ is generated |
|
web/src/ |
React SPA: connect.ts clients, auth-state.ts, hooks/, contexts/, components/, themes/ |
Layering rules, enforced by depguard in .golangci.yaml:
- Imports point downward only:
cmd→server→core→store→ {provider,markdown,filter} →internal. internal/packages import nothing from this module (exceptinternal/testutil, which may importproto/gen).- A package goes in
internal/only if it could be published as a standalone module unchanged. If it knows what a Memo, Space, or instance setting is, it is a root package. - Never create
util,common,base,helpers,misc, orpkgpackages. Name a package for the one thing it does, or put the code next to its only caller. - New business rules extracted from
server/apigo tocore/<resource>/, one package per resource.
File convention inside server/api/: <resource>_service.go holds the RPC handlers, <resource>_service_converters.go the
store↔proto conversion, and <resource>_service_<topic>.go further splits. Extend the matching file; do not add *_helpers2.go.
Black-box service tests live in server/api/test/, unit tests next to the code.
| Change | Update | Verify |
|---|---|---|
| Go service or router behavior | Handlers under server/, rules under core/, tests near package |
go test -v -race ./server/... ./core/... |
| Store or migration behavior | store/, all three DB driver migrations, LATEST.sql |
go test -v ./store/... |
| Markdown, filter, or provider logic | markdown/, filter/, provider/ |
go test -v -race ./markdown/... ./filter/... ./provider/... |
| Internal package logic | Relevant internal/ package tests |
go test -v -race ./internal/... |
| Frontend behavior | Components/hooks/contexts under web/src/ |
cd web && pnpm lint && pnpm test |
| Frontend production output | Vite config or release-sensitive UI | cd web && pnpm build or pnpm release |
| Proto API | .proto source, generated outputs, proto/api/CHANGELOG.md for breaks |
cd proto && buf generate && buf lint |
| Public unauthenticated route | server/api/acl_config.go |
Targeted server test or manual route check |
- Wrap errors with
errors.Wrap(err, "context")fromgithub.com/pkg/errors; do not usefmt.Errorf. - Return service errors with
status.Errorf(codes.X, "message"). - Keep imports grouped as stdlib, third-party, then
github.com/usememos/memos; goimports is run by golangci-lint. - Add doc comments for exported identifiers; godot enforces exported comment punctuation.
- Avoid package-level mutable state unless the surrounding package already uses that pattern.
- Use
@/for absolute imports. - Follow Biome formatting: 2-space indent, double quotes, semicolons, 140-character line width.
- Put server data in React Query hooks under
web/src/hooks/; keep UI-only state in contexts or component state. - Use Tailwind CSS v4 utilities,
cn()for class merging, and CVA for variants. - Reuse Base UI primitives and existing components before adding new UI primitives.
- Keep generated proto TypeScript under
web/src/types/proto/out of manual edits and Biome rewrites.
- Schema changes require SQLite, MySQL, and PostgreSQL migrations plus
LATEST.sqlupdates. - Fresh-install SQL and incremental migrations must stay equivalent.
- Proto field changes must preserve compatibility unless the task explicitly allows a breaking API change. Record every
allowed break in
proto/api/CHANGELOG.md; compatibility is checked during review. - Regenerate after proto edits and include both Go/OpenAPI and TypeScript generated outputs.
- Run the narrowest relevant checks while iterating.
- Before finishing, run the checks that match the changed surface from "Change Routing".
- For docs-only changes,
git diff --checkis sufficient unless the docs include runnable examples that should be tested. - If a required check cannot run locally, report the reason and the exact command that remains.
-
Releases are cut by pushing a
YY.MM.N[-rc.K]tag. To cut, promote, or check a release, follow.agents/skills/release/SKILL.md. -
Development builds use the HEAD committer date in UTC as
YY.MM.D-dev.<sha7>; release builds inject the CalVer tag. -
go buildembeds Git metadata automatically;go runneeds-buildvcs=true. Builds without Git metadata must injectgithub.com/usememos/memos/internal/version.Versionthrough-ldflags. -
Docker excludes
.git; pass--build-arg VERSION="$(bash scripts/release_version.sh development-version)"and--build-arg COMMIT="$(git rev-parse HEAD)"when building locally (build frontend assets first). -
Backend CI: Go 1.27.2,
go mod tidy -go=1.27.2,go fix -diff ./..., golangci-lint v2.14.0 (including goimports), test groupsstore,server,internal,other(cmd,core,markdown,filter,provider,proto). -
Frontend CI: Node 24, pnpm 11.0.1,
pnpm lint,pnpm test,pnpm build. -
Proto CI:
buf lintandbuf formatcheck. -
Docker:
scripts/Dockerfile, Alpine 3.21 runtime, non-root user, port 5230, multi-arch amd64/arm64/arm/v7.