11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Memos is a lightweight, self-hosted knowledge management and note-taking platform. The architecture pairs a Go backend with a React+Vite frontend, using gRPC for internal communication and providing REST API access via gRPC-Gateway.
Development Commands
Backend (Go)
Start Development Server:
go run ./cmd/memos --mode dev --port 8081
Build Binary:
go build ./cmd/memos
Run Tests:
go test ./... # All tests
go test ./store/... # Store layer tests only
go test ./server/router/api/v1/test/... # API tests
Lint:
golangci-lint run # Full lint check (uses .golangci.yaml)
Generate Protocol Buffers:
cd proto
buf generate # Generate Go/TypeScript from .proto files
buf format -w # Format proto files
Frontend (React + Vite)
Install Dependencies:
cd web
pnpm install
Development Server:
cd web
pnpm dev # Hot-reload dev server (typically :5173)
Build:
cd web
pnpm build # Build to web/dist/
pnpm release # Build and copy to server/router/frontend/dist/
Lint:
cd web
pnpm lint # TypeScript check + ESLint
CLI Flags and Environment Variables
The backend accepts the following configuration via flags or MEMOS_* environment variables:
--mode/MEMOS_MODE- Runtime mode:dev,prod,demo(default:prod)--addr/MEMOS_ADDR- Bind address (default:0.0.0.0)--port/MEMOS_PORT- HTTP/gRPC port (default:5230)--unix-sock/MEMOS_UNIX_SOCK- Unix socket path (optional)--data/MEMOS_DATA- Data directory for SQLite (default:~/.memos)--driver/MEMOS_DRIVER- Database driver:sqlite,mysql,postgres(default:sqlite)--dsn/MEMOS_DSN- Database connection string (for MySQL/PostgreSQL)--instance-url/MEMOS_INSTANCE_URL- Public instance URL for webhooks/OAuth
Architecture
High-Level Structure
cmd/memos/ # CLI entry point, starts HTTP+gRPC server
server/ # HTTP server and routing
├── router/api/v1/ # gRPC services + gRPC-Gateway REST handlers
├── router/frontend/ # Embedded SPA static file serving
└── router/rss/ # RSS feed generation
internal/ # Shared domain logic and utilities
├── base/ # Base types and constants
├── profile/ # Configuration profile handling
├── util/ # Utility functions
└── version/ # Version information
store/ # Data persistence layer (repository pattern)
├── db/ # Database driver implementations
│ ├── sqlite/ # SQLite driver + migrations
│ ├── mysql/ # MySQL driver + migrations
│ └── postgres/ # PostgreSQL driver + migrations
└── cache/ # In-memory caching
proto/ # Protocol Buffer definitions
└── api/v1/ # API service contracts (.proto files)
web/ # Frontend React application
├── src/
│ ├── components/ # React components
│ ├── pages/ # Page-level components
│ ├── store/ # MobX state management
│ ├── router/ # React Router configuration
│ ├── locales/ # i18n translation files
│ ├── hooks/ # Custom React hooks
│ └── utils/ # Utility functions
└── public/ # Static assets
API Architecture
Dual Protocol Serving:
The server uses cmux (connection multiplexer) to serve both gRPC and HTTP on the same port:
- HTTP/2 +
application/grpc→ Native gRPC server - HTTP/1.1 → Echo server (REST API via gRPC-Gateway, static files, RSS)
API Services (defined in proto/api/v1/*.proto):
WorkspaceService- Workspace settings and profilesAuthService- Authentication and session managementUserService- User managementMemoService- Core memo CRUD operationsAttachmentService- File uploads and storageInboxService- Inbox itemsActivityService- Activity loggingMarkdownService- Markdown utilities (link metadata, etc.)IdentityProviderService- OAuth/SSO providersShortcutService- User shortcuts
API Access Methods:
- Native gRPC - Direct gRPC calls to
memos.api.v1.*services - REST API - HTTP REST at
/api/v1/*(via gRPC-Gateway) - gRPC-Web - Browser gRPC calls to
/memos.api.v1.*(via grpc-web proxy)
Database Layer
Store Interface:
The store.Driver interface (store/driver.go) defines all data access methods. Three implementations exist:
store/db/sqlite/- SQLite driver (default, embedded database)store/db/mysql/- MySQL driverstore/db/postgres/- PostgreSQL driver
Migrations:
Each driver contains its own migration files in subdirectories. Schema version tracking is stored in workspace_setting (key: bb.general.version). The store/migrator.go orchestrates migrations across all drivers.
Key Models:
Memo- Core note/memo entityUser- User accountsAttachment- Uploaded files and imagesMemoRelation- Graph relationships between memosActivity- Activity log entriesInbox- Inbox itemsReaction- Emoji reactionsWorkspaceSetting- Workspace-level configurationUserSetting- User preferencesIdentityProvider- OAuth/SSO provider configs
Frontend Architecture
Tech Stack:
- Framework: React 18 with TypeScript
- Build Tool: Vite 7
- Routing: React Router 7
- State Management: MobX
- Styling: Tailwind CSS 4 + Emotion
- UI Components: Radix UI primitives
- i18n: react-i18next with language files in
web/src/locales/
State Management:
MobX stores in web/src/store/ handle global state:
userStore- Current user sessionmemoStore- Memo collection and filterseditorStore- Memo editor statedialogStore- Modal dialogs- etc.
gRPC-Web Client:
The frontend uses nice-grpc-web to call backend services. Client setup is in web/src/grpcweb.ts.
Authentication
Dual Auth Support:
- Session-based (Cookie):
user_sessioncookie with format{userID}-{sessionID} - Token-based (JWT):
Authorization: Bearer <token>header
Flow:
- Authentication interceptor (
server/router/api/v1/acl.go) runs on all gRPC methods - Public endpoints bypass auth (see
acl_config.gofor allowlist) - Context values set:
userIDContextKey,sessionIDContextKey,accessTokenContextKey - Sessions have sliding expiration (14 days from last access)
Code Style and Conventions
Go
- Formatting: All code must be
gofmt-compliant (tabs for indentation) - Imports: Group stdlib, external, and local imports (enforced by
goimports) - Error Handling: Wrap errors with
%wwhen propagating:errors.Wrap(err, "context") - Naming: Package names lowercase, no underscores
- Linting: Enforced via
.golangci.yaml(revive, staticcheck, gocritic, etc.)
TypeScript/React
- Components: PascalCase filenames (e.g.,
MemoEditor.tsx) - Hooks: camelCase filenames (e.g.,
useMemoList.ts) - Formatting: Prettier enforced (see
web/.prettierrc.js) - Import Ordering: Managed by
@trivago/prettier-plugin-sort-imports - Styling: Tailwind utility classes preferred over custom CSS
Commit Messages
Follow Conventional Commits format:
feat(scope): description- New featuresfix(scope): description- Bug fixeschore(scope): description- Maintenance tasksrefactor(scope): description- Code restructuringtest(scope): description- Test additions/fixesdocs(scope): description- Documentation updates
Scopes: server, api, store, web, proto, etc.
Testing
Go Tests:
- Test files:
*_test.goalongside source files - Run specific package:
go test ./store/cache/... - API integration tests:
server/router/api/v1/test/*_test.go - Prefer table-driven tests for multiple test cases
Frontend: Currently relies on linting and manual testing. For UI changes, validate with local dev server.
Protocol Buffer Workflow
Prerequisites: Install buf
Modifying APIs:
- Edit
.protofiles inproto/api/v1/ - Run
cd proto && buf generateto regenerate Go and TypeScript code - Update service implementations in
server/router/api/v1/ - Update frontend gRPC-Web clients in
web/src/
Generated Code Locations:
- Go:
proto/gen/api/v1/ - TypeScript:
web/src/types/proto/api/v1/
Database Migrations
When adding database schema changes:
-
Create migration file in driver-specific directory:
- SQLite:
store/db/sqlite/migration/ - MySQL:
store/db/mysql/migration/ - PostgreSQL:
store/db/postgres/migration/
- SQLite:
-
Follow naming:
prod_YYYYMMDD_description.sql -
Update schema version constant in
store/migrator.go -
Test migration locally with all three database drivers
Common Patterns
Adding a New API Endpoint
- Define service method in
proto/api/v1/{service}_service.proto - Run
buf generateto regenerate code - Implement method in
server/router/api/v1/{service}_service.go - Add to public allowlist in
acl_config.goif unauthenticated - Update frontend client in
web/src/
Adding a New Data Model
- Define struct in
store/{model}.go - Add CRUD methods to
store.Driverinterface instore/driver.go - Implement methods in each driver:
store/db/sqlite/{model}.gostore/db/mysql/{model}.gostore/db/postgres/{model}.go
- Create migration files for schema changes
- Add tests in
store/test/{model}_test.go
Frontend Data Fetching
Use gRPC-Web client from web/src/grpcweb.ts:
import { memoServiceClient } from "@/grpcweb";
const response = await memoServiceClient.listMemos({
filter: "creator == 'users/1'",
});
State is typically managed in MobX stores (web/src/store/).
Production Deployment
Docker (Recommended):
docker run -d \
--name memos \
-p 5230:5230 \
-v ~/.memos:/var/opt/memos \
neosmemo/memos:stable
From Source:
- Build frontend:
cd web && pnpm install && pnpm release - Build backend:
go build -o memos ./cmd/memos - Run:
./memos --mode prod --port 5230
Data Directory:
For SQLite (default), all data is stored in the directory specified by --data flag. This includes:
memos_prod.db- SQLite databaseassets/- Uploaded files (unless using S3-compatible storage)
Key Dependencies
Backend:
github.com/spf13/cobra- CLI frameworkgithub.com/spf13/viper- Configuration managementgoogle.golang.org/grpc- gRPC servergithub.com/grpc-ecosystem/grpc-gateway/v2- REST gatewaygithub.com/labstack/echo/v4- HTTP servergithub.com/soheilhy/cmux- Connection multiplexingmodernc.org/sqlite- Pure Go SQLite drivergithub.com/golang-jwt/jwt/v5- JWT authentication
Frontend:
react/react-dom- UI frameworkreact-router-dom- Routingmobx/mobx-react-lite- State managementtailwindcss- Stylingnice-grpc-web- gRPC-Web client@radix-ui/*- Headless UI componentsreact-i18next- Internationalization