Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# EditorConfig is awesome: https://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

# Go backend (gofmt uses tabs)
[*.go]
indent_style = tab
# Leave indent_size unset so each editor can display tabs as preferred

# Frontend TypeScript/JavaScript – matches frontend/.prettierrc (tabWidth 4, useTabs false)
[*.{ts,tsx,js,jsx,mjs,cjs}]
indent_style = space
indent_size = 4

# JSON and similar config files
[*.{json,jsonc,prettierrc,eslintrc}]
indent_style = space
indent_size = 4

# YAML (docker-compose, CI, etc.) – 2 spaces is the de-facto standard
[*.{yml,yaml}]
indent_style = space
indent_size = 2

# Markdown and docs – keep hard line-break spaces
[*.md]
indent_style = space
indent_size = 4
trim_trailing_whitespace = false

# Shell scripts
[*.{sh,bash}]
indent_style = space
indent_size = 4

# SQL migrations and other SQL
[*.sql]
indent_style = space
indent_size = 4
119 changes: 57 additions & 62 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25.3'
cache-dependency-path: backend/go.sum
- name: Install mise
uses: jdx/mise-action@v2

- name: Install tools with mise
run: mise install

- name: Cache Go modules
uses: actions/cache@v4
Expand All @@ -31,55 +31,41 @@ jobs:
restore-keys: |
${{ runner.os }}-go-

- name: Check gofmt
run: ./scripts/check.sh --check gofmt
- name: Build check tool
run: go build -o check .
working-directory: ./scripts/check
env:
GOTOOLCHAIN: auto

- name: Check gofmt
run: ./scripts/check/check --check gofmt --ci

- name: Check go mod tidy
run: ./scripts/check.sh --check go-mod-tidy
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check go-mod-tidy --ci

- name: Run govulncheck
run: ./scripts/check.sh --check govulncheck
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check govulncheck --ci

- name: Run go vet
run: ./scripts/check.sh --check go-vet
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check go-vet --ci

- name: Run staticcheck
run: ./scripts/check.sh --check staticcheck
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check staticcheck --ci

- name: Run ineffassign
run: ./scripts/check.sh --check ineffassign
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check ineffassign --ci

- name: Run misspell
run: ./scripts/check.sh --check misspell
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check misspell --ci

- name: Run gocyclo
run: ./scripts/check.sh --check gocyclo
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check gocyclo --ci

- name: Run nilaway
run: ./scripts/check.sh --check nilaway
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check nilaway --ci

- name: Run backend tests
run: ./scripts/check.sh --check backend-tests
env:
GOTOOLCHAIN: auto
run: ./scripts/check/check --check backend-tests --ci

frontend-checks:
name: Frontend (TypeScript)
Expand All @@ -89,30 +75,44 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install mise
uses: jdx/mise-action@v2

- name: Install tools with mise
run: mise install

- name: Set up Node.js
uses: actions/setup-node@v4
- name: Cache pnpm
uses: actions/cache@v4
with:
node-version: '25'
cache: 'pnpm'
cache-dependency-path: ./frontend/pnpm-lock.yaml
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-${{ hashFiles('frontend/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-

- name: Install dependencies
run: pnpm install --frozen-lockfile
working-directory: ./frontend

- name: Cache Go modules
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('backend/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

- name: Build check tool
run: go build -o check .
working-directory: ./scripts/check

- name: Check Prettier
run: ./scripts/check.sh --check prettier
run: ./scripts/check/check --check prettier --ci

- name: Run ESLint
run: ./scripts/check.sh --check eslint
run: ./scripts/check/check --check eslint --ci

- name: Run frontend tests
run: ./scripts/check.sh --check frontend-tests
run: ./scripts/check/check --check frontend-tests --ci

e2e-tests:
name: E2E Tests (Playwright)
Expand All @@ -123,17 +123,19 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install mise
uses: jdx/mise-action@v2

- name: Install tools with mise
run: mise install

- name: Set up Node.js
uses: actions/setup-node@v4
- name: Cache pnpm
uses: actions/cache@v4
with:
node-version: '25'
cache: 'pnpm'
cache-dependency-path: ./frontend/pnpm-lock.yaml
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-${{ hashFiles('frontend/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-

- name: Install dependencies
run: pnpm install --frozen-lockfile
Expand All @@ -143,12 +145,6 @@ jobs:
run: pnpm exec playwright install --with-deps chromium
working-directory: ./frontend

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25.3'
cache-dependency-path: backend/go.sum

- name: Cache Go modules
uses: actions/cache@v4
with:
Expand All @@ -162,5 +158,4 @@ jobs:
working-directory: ./frontend
env:
VMAIL_TEST_MODE: 'true'
GOTOOLCHAIN: auto
PLAYWRIGHT: '1'
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,10 @@ docker-compose.override.yaml

# Playwright
frontend/playwright-report/
frontend/test-results/
frontend/test-results/

# Air's logs and the built Go binary
/backend/tmp

# Check script binary
/scripts/check/check
34 changes: 0 additions & 34 deletions .replit

This file was deleted.

94 changes: 94 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Project overview

This project is V-Mail.
V-Mail is a self-hosted, web-based email client designed for personal use.
It uses the layout and keyboard shortcuts of Gmail to make it immediately familiar for ex-Gmail users.
It connects to an IMAP server and provides the web UI to read and send email.

## Non-goals

Compared to Gmail, this project does **not** include:

* Client-side email filters. The user should set these up on the server, typically via [Sieve](http://sieve.info/).
* A visual query builder for the search box. A simple text field is fine.
* A multi-language UI. The UI is English-only.
* 95% of Gmail's settings. V-Mail has some basic settings like emails per page and undo send delay, but that's it.
* Automatic categorization such as primary/social/promotions.
* The ability to collapse the left sidebar.
* Signature management.
* Smiley/emoji reactions to emails. This is Google's proprietary thing.

## Tech stack

V-Mail uses a **Postgres** database, a **Go** back end, a **REST** API, and a **React** front end with **TypeScript**.
V-Mail needs a separate, self-hosted [Authelia](https://www.authelia.com) instance for authentication.

# AI Context & rules

## AI dev process

### Dev process

Always follow this process when developing in this project:

1. Before developing a feature, make sure to do the planning and know exactly what you want to achieve and have a task list.
2. Before touching code of a specific domain, read the related docs within `/docs` or similar.
List folders if you need to. Example: `docs/backend/auth.md` for auth logic, `backend/migrations/*.sql` for DB schema.
3. Do the changes, in small iterations. Adhere to the [style guide](docs/style-guide.md)!
4. Use `./scripts/check.sh` to check that everything is still working.
- Or use a subset, for example, if you only touch the front end.
- Even fix gocyclo's cyclomatic complexity warnings! I know it's a pain, but it's helpful to keep Go funcs simple.
5. Make sure to add tests for the new code. Think about unit tests, integration tests, and end-to-end tests.
6. Check if you added new patterns, external dependencies, or architectural changes. Update all related docs.
7. Also consider updating `AGENTS.md` (incl. this very process) to keep the next agent's work streamlined.
8. Before you call it done, see the diff of your changes (`git diff`) and make sure all changes are actually
needed. Revert unneeded changes.
9. Rerun `./scripts/check.sh` to make sure everything still works.
10. Suggest a commit message, in the format seen in the style guide.

Always keep the dev process and style guide in mind.

## High-level map

- **Frontend**: React 19 + Vite. Entry: `frontend/src/main.tsx`. State: Zustand.
- **Backend**: Go 1.25 (Standard lib HTTP). Entry: `backend/cmd/server`.
- **Architecture**: Handlers (`api/`) -> Service/core logic (`internal/`) -> DB (`db/`).
- **Testing**: Playwright (E2E), Vitest (unit), Go `testing` package, `testify` for assertions and mocking support,
`mockery` for generating mocks.

## Tooling

- Tool versions: We use [mise](https://mise.jdx.dev). Go, Node, pnpm, etc. are in `mise.toml`. `mise install` installs.
- `scripts/check.sh` is the primary quality control tool. It runs linting, formatting, and back/front end+E2E tests.
`./scripts/check.sh --frontend`, `./scripts/check.sh --backend` are options, and so is
`./scripts/check.sh --check <check-name>` (use `./scripts/check.sh --help` to list them).
- Front end: `pnpm lint`, `pnpm lint:fix`, `pnpm format`, `pnpm test`, and `pnpm test:e2e`.
- `pnpm exec playwright test --config=../playwright.config.ts --grep "{test-name}" is also helpful
for context-efficient re-running of E2E-tests.
- `migrate up` (using golang-migrate)
- For recurring updates of tools, dependencies, and infra, see `docs/maintenance.md`.

## "Red line" rules (do not break)
- Always make check.sh happy, including warnings and gocyclo complexity!
- Modular architecture, no global state, no package cycles.
- Sentence case titles and labels
- If you add a new tool, script, or architectural pattern, you MUST update this file (`AGENTS.md`) and any relevant docs
before finishing your response.

## Style guide highlights

### General
- **Commits**: First line max 50 chars. Blank line. Detailed description, usually as concise bullets.
- **Writing**: Friendly, active voice, and ALWAYS sentence case titles and labels!
- **Complexity**: Max cyclomatic complexity of 15 per function. (checked by gocyclo)

### Frontend (TypeScript/React)
- **No classes**: Use modules and functional components only.
- **Strict types**: `no-explicit-any` is enforced.
- **Imports**: Organized by groups (builtin, external, internal, parent, sibling, index).
- **Formatting**: Prettier is the authority.
- **React**: Hooks rules enforced, no dangerous HTML.

### Backend (Go)
- **Comments**: Meaningful comments for public functions/types.
- **DB**: Plural table names, singular columns. Add comments in SQL and Go structs.
9 changes: 1 addition & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,8 +1 @@
Before touching the repo, make sure you've read:
- the main [README.md](README.md)
- [CONTRIBUTING.md](CONTRIBUTING.md)
- [docs/style-guide](docs/style-guide.md)

Then read any other docs you might need as you go, for example, [docs/testing.md](docs/testing.md) if you touch tests.

Always keep the dev process and style guide in mind.
@AGENTS.md
Loading