Skip to content

Contributing ​

This section is for people who change the Mankomail source code: fix a bug, add a node, connect a new service, improve the interface or the documentation. It describes the repository as it is: the packages, the tools, the commands, and the rules that the tooling enforces.

The other pages of the section go deeper:

PageWhen to read it
Writing a nodeYou add or change a node of the catalog.
Writing an integrationYou connect a third-party service (API key, webhooks, polling trigger).
ConventionsBefore your first change: TypeScript, package boundaries, i18n, API errors, commits.
TestingYou write or run tests, including the ones that need PostgreSQL.

Repository layout ​

The repository is a single pnpm workspace driven by Turborepo. The workspace members are every folder under packages/ and apps/ (pnpm-workspace.yaml). All packages are pure ES modules written in TypeScript, published under the @manko/ scope inside the workspace.

FolderPackageRole
packages/workflow@manko/workflowThe isomorphic core: workflow graph and validation, the node contract (defineNode), parameter declarations, the {{ }} templating engine. Depends on no other package of the product.
packages/api-types@manko/api-typesThe zod schemas shared by the server (validation) and the interface (types): API payloads, error format, connection catalog, integration declarations.
packages/credentials@manko/credentialsCredential types, instance encryption, OAuth flows and token refresh.
packages/connectors@manko/connectorsGmail, Microsoft Graph and IMAP/SMTP behind one common contract.
packages/mirror@manko/mirrorMailbox synchronisation: cursors, backfill, push, and the canonical message model.
packages/nodes@manko/nodesThe node catalog: each node's declaration and execute function, the service contracts nodes consume, test doubles.
packages/engine@manko/engineThe run engine: persisted steps, queue, scheduler, retries, idempotency.
packages/tools@manko/toolsThe internal tool layer: typed, named capabilities with a declared effect, used by the mailbox analyzer.
packages/server@manko/serverThe composition point: HTTP and WebSocket API, typed configuration, database access and SQL migrations, integrations.
packages/ui@manko/uiThe Vue 3 application: workflow editor, webmail, administration. Built, then served by the server.
apps/docs@manko/docsThis documentation (VitePress). Node and integration pages are generated from the real catalog.

Dependencies between packages only go downwards, and ESLint enforces it. For example, @manko/nodes may import only @manko/workflow, and @manko/ui only @manko/workflow, @manko/api-types and @manko/nodes. The full table is in Conventions.

Prerequisites ​

ToolVersionWhere it is pinned
Node.js24 or laterengines in the root package.json; CI runs Node 24.
pnpm11.24.0packageManager in the root package.json. Run corepack enable and Corepack provides the pinned version.
DockerRecent Docker Engine with ComposeRuns PostgreSQL 16 and MinIO for the development stack and the integration tests.

There is no .nvmrc file: the engines field is the reference. TypeScript, Vitest, ESLint, Biome and Turborepo are development dependencies of the workspace; you do not install them globally.

pnpm install runs no dependency lifecycle scripts: the onlyBuiltDependencies list in pnpm-workspace.yaml is empty on purpose, and CI fails if pnpm reports ignored build scripts. Adding a dependency that needs a build script is an exception to justify in that file.

Set up a development environment ​

  1. Clone the repository and install the dependencies:

    sh
    corepack enable
    pnpm install
  2. Copy the example configuration. Every variable is documented in it and in Environment variables.

    sh
    cp .env.example .env
  3. Choose how to run the application.

    The full stack in containers. compose.yaml starts PostgreSQL 16, MinIO, a job that creates the bucket, and the application built from the Dockerfile, on port 3000:

    sh
    docker compose up -d
    curl localhost:3000/healthz   # the process is alive
    curl localhost:3000/readyz    # database reachable, migrations applied

    The server and the interface from source, which is what you want while changing code. Start only the dependencies from compose.yaml, then the two dev servers:

    sh
    docker compose up -d postgres minio createbucket
    pnpm build
    pnpm --filter @manko/server dev    # node --watch, port 3000
    pnpm --filter @manko/ui dev        # Vite dev server

    Open the URL printed by Vite. The Vite dev server proxies /api (WebSocket included), /hooks, /healthz and /readyz to http://localhost:3000, so the session cookie works on a single origin.

Configuration of the server started from source

The server reads its configuration from the process environment only; it does not load .env by itself. Export the variables in the shell that starts it, for example with set -a; . ./.env; set +a.

compose.yaml publishes PostgreSQL on 127.0.0.1:15432 and MinIO on 127.0.0.1:19000, which are not the ports written in .env.example. When you run the server from source against these containers, adjust DATABASE_URL and STORAGE_ENDPOINT accordingly. The credentials of these development containers are in compose.yaml.

On first start with an empty database, the server creates the first administrator from BOOTSTRAP_ADMIN_EMAIL and BOOTSTRAP_ADMIN_PASSWORD when they are set. The setting is ignored as soon as a member exists.

Main commands ​

Run these from the repository root.

CommandWhat it does
pnpm buildBuilds every package with Turborepo (turbo run build), dependencies first. The documentation package is built too, which runs its generator.
pnpm typecheckType-checks every package (turbo run typecheck).
pnpm lintRuns ESLint (TypeScript rules, package boundaries, forbidden imports) then biome check (formatting and import order).
pnpm lint:fixSame, applying the automatic fixes.
pnpm formatReformats the code with Biome.
pnpm testRuns Vitest on every project (vitest run). See Testing.
pnpm test:watchVitest in watch mode.
pnpm devRuns the dev script of every package that has one (turbo run dev): server, interface and documentation.
pnpm cleanRemoves build outputs and caches.

Commands for a single package go through --filter, for example pnpm --filter @manko/docs dev to serve this documentation locally. The documentation commands are detailed in Writing a node.

Before you hand in a change, run at least pnpm typecheck, pnpm lint and the tests of the packages you touched. CI runs the same checks on every push and pull request.

What CI checks ​

The CI workflow runs one job on every push to main and on every pull request:

  1. pnpm install --frozen-lockfile, and a check that no dependency build script was ignored;
  2. pnpm typecheck;
  3. pnpm lint;
  4. pnpm test against a real PostgreSQL 16 and a real MinIO, followed by a guard that fails if any test was skipped or if the number of tests falls below a floor;
  5. pnpm build;
  6. a smoke test: docker compose up of the full stack, then /healthz and /readyz must answer.

A scheduled job runs the full test suite three times in a row every night to detect unstable tests.

Licence ​

Check the license field of the package.json of the package you change before contributing. @manko/workflow and @manko/api-types declare Apache-2.0. The other packages declare SEE LICENSE IN LICENSE.md, which refers to a licence file at the root of the repository; that file is authoritative for them.