From 9f68960eda213342d4adbc9c01e3ba4df6efe9fa Mon Sep 17 00:00:00 2001
From: Siddharth Kumar Sah
Date: Sat, 28 Mar 2026 12:14:24 +0800
Subject: [PATCH] docs: rewrite README, add CONTRIBUTING.md, developer and
translation guides
Rewrite README to remove AI writing patterns (em dashes, promotional
language, vague claims). Make Quick Start section explicit about default
credentials and forced password change. Add Contributing section linking
to CONTRIBUTING.md, developer guide, and translation guide.
Create CONTRIBUTING.md with issue guidelines, PR workflow, commit
conventions, and development setup. Add developer guide (dev setup,
project structure, how to add a tool) and translation guide (how the
i18n system works, step-by-step for adding a language) to VitePress
docs. Register both new pages in the docs sidebar.
---
CONTRIBUTING.md | 92 ++++++++++++++
README.md | 56 +++++----
apps/docs/.vitepress/config.mts | 2 +
apps/docs/guide/developer.md | 204 ++++++++++++++++++++++++++++++++
apps/docs/guide/translations.md | 103 ++++++++++++++++
5 files changed, 436 insertions(+), 21 deletions(-)
create mode 100644 CONTRIBUTING.md
create mode 100644 apps/docs/guide/developer.md
create mode 100644 apps/docs/guide/translations.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..9350f7eb
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,92 @@
+# Contributing to Stirling Image
+
+Thanks for your interest in contributing. There are many ways to help beyond writing code: reporting bugs, suggesting features, improving docs, and adding translations.
+
+## Issues
+
+Before opening an issue, search existing ones to avoid duplicates.
+
+- **Bug reports**: Include steps to reproduce, expected vs. actual behavior, and your environment (OS, Docker version, browser).
+- **Feature requests**: Describe the problem you want solved, not just the solution. Context helps.
+- **Questions**: Open an issue. We will do our best to respond quickly.
+
+## Pull requests
+
+1. **Open an issue first.** Describe what you want to change and why. Wait for a maintainer to confirm the direction before writing code.
+2. **Fork the repo** and create a branch from `main`.
+3. **Make your changes.** Follow the conventions in [CLAUDE.md](CLAUDE.md) (formatting, file structure, commit style).
+4. **Test your changes.** Run `pnpm test` and `pnpm lint` before pushing. If you changed UI, run `pnpm test:e2e` too.
+5. **Submit a PR.** Reference the issue number. Keep the title short and descriptive.
+
+### Commit messages
+
+We use [Conventional Commits](https://www.conventionalcommits.org/) for automated releases:
+
+- `feat:` new feature (triggers a minor version bump)
+- `fix:` bug fix (triggers a patch version bump)
+- `docs:` documentation only
+- `test:` adding or fixing tests
+- `refactor:` code change that doesn't fix a bug or add a feature
+- `chore:` maintenance (CI, deps, config)
+
+Example: `feat: add HEIC to PNG conversion support`
+
+### What makes a good PR
+
+- One logical change per PR. If you need to refactor something to add a feature, that can be one PR, but don't mix unrelated changes.
+- Clear commit messages that explain why, not just what.
+- Tests for new behavior when possible.
+- No unrelated formatting changes (Biome handles formatting).
+
+## Development setup
+
+Full instructions are in the [Developer Guide](https://siddharthksah.github.io/Stirling-Image/guide/developer). The short version:
+
+```bash
+git clone https://github.com/siddharthksah/Stirling-Image.git
+cd Stirling-Image
+pnpm install
+pnpm dev # starts both frontend and backend
+```
+
+The frontend runs at http://localhost:1349 and proxies API calls to the backend on port 13490.
+
+### Running tests
+
+```bash
+pnpm lint # Biome lint + format check
+pnpm typecheck # TypeScript across all workspaces
+pnpm test # unit + integration tests
+pnpm test:e2e # Playwright end-to-end tests
+```
+
+All of these run in CI on every PR. Make sure they pass locally first.
+
+## Adding a new tool
+
+Tools follow a consistent pattern. You will need to touch three places:
+
+1. **Backend route** in `apps/api/src/routes/tools/` using `createToolRoute()` from the tool factory.
+2. **Frontend settings component** in `apps/web/src/components/tools/` with the tool's UI controls.
+3. **i18n entry** in `packages/shared/src/i18n/en.ts` with the tool's name and description.
+
+See the [Developer Guide](https://siddharthksah.github.io/Stirling-Image/guide/developer) for a walkthrough.
+
+## Translations
+
+We currently ship English only, but the i18n system is designed for easy extension. If you want to add a language, see the [Translation Guide](https://siddharthksah.github.io/Stirling-Image/guide/translations).
+
+## Code style
+
+Biome handles formatting and linting. The rules are in `biome.json` and enforced by a pre-commit hook. Don't modify the Biome or TypeScript config files to silence warnings. Fix the code instead.
+
+Quick summary:
+
+- Double quotes, semicolons, 2-space indentation
+- ES modules everywhere
+- Zod for API input validation
+- No `any` types without justification
+
+## License
+
+By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
diff --git a/README.md b/README.md
index 81976620..c684cea9 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
-
Stirling Image - The Open-Source Image Processing Platform
+
Stirling Image
-Stirling Image is a powerful, open-source image processing platform. Self-host it in a single Docker container with a private API. Resize, compress, convert, remove backgrounds, upscale, run OCR, and more — without sending images to external services.
+
Open-source, self-hosted image processing. One Docker container, no cloud dependencies.
@@ -15,45 +15,59 @@ Stirling Image is a powerful, open-source image processing platform. Self-host i

-## Key Capabilities
+## What it does
-- **33+ image tools** — Resize, crop, compress, convert, watermark, OCR, and more.
-- **AI-powered** — Background removal, upscaling, object erasing, face blurring — all running locally.
-- **Automation & workflows** — Chain tools into reusable pipelines. Batch process up to 200 images at once.
-- **Developer platform** — REST API for every tool. Swagger docs included.
-- **Your data stays yours** — No telemetry, no tracking, no cloud. Single Docker container on any architecture.
+Resize, crop, compress, convert, watermark, OCR, remove backgrounds, upscale, erase objects, blur faces, and more. 33+ tools in total, all running locally on your hardware.
-For a full feature list, see the docs: **https://siddharthksah.github.io/Stirling-Image/**
+You can chain tools into reusable pipelines and batch process up to 200 images at once. Every tool is also available through a REST API with Swagger docs.
-## Quick Start
+No telemetry, no tracking, no external calls. Your images never leave your machine.
+
+## Quick start
```bash
docker run -d -p 1349:1349 -v stirling-data:/data ghcr.io/siddharthksah/stirling-image:latest
```
-Then open: http://localhost:1349. Default login: `admin` / `admin`.
+Open http://localhost:1349 in your browser.
-For full installation options, see the [Getting Started Guide](https://siddharthksah.github.io/Stirling-Image/guide/getting-started).
+**Default credentials:**
-## Resources
+| Field | Value |
+|----------|---------|
+| Username | `admin` |
+| Password | `admin` |
-- [**Documentation**](https://siddharthksah.github.io/Stirling-Image/)
-- [**API Docs**](https://siddharthksah.github.io/Stirling-Image/api/rest)
-- [**Configuration**](https://siddharthksah.github.io/Stirling-Image/guide/configuration)
+You will be asked to change your password on first login. This is enforced for all new accounts and cannot be skipped in production.
+
+For Docker Compose, persistent storage, and other setup options, see the [Getting Started Guide](https://siddharthksah.github.io/Stirling-Image/guide/getting-started).
+
+## Documentation
+
+- [Getting started](https://siddharthksah.github.io/Stirling-Image/guide/getting-started)
+- [Configuration](https://siddharthksah.github.io/Stirling-Image/guide/configuration)
+- [REST API](https://siddharthksah.github.io/Stirling-Image/api/rest)
+- [Architecture](https://siddharthksah.github.io/Stirling-Image/guide/architecture)
+- [Developer guide](https://siddharthksah.github.io/Stirling-Image/guide/developer)
+- [Translation guide](https://siddharthksah.github.io/Stirling-Image/guide/translations)
+
+## Contributing
+
+We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
+
+For development setup, see the [Developer Guide](https://siddharthksah.github.io/Stirling-Image/guide/developer).
+
+For adding translations, see the [Translation Guide](https://siddharthksah.github.io/Stirling-Image/guide/translations).
## Support
-- **Bug Reports**: [GitHub Issues](https://github.com/siddharthksah/Stirling-Image/issues)
+Bug reports and feature requests: [GitHub Issues](https://github.com/siddharthksah/Stirling-Image/issues)
-## Contributing
-
-Contributions welcome. Open an issue first so we can talk about what you have in mind.
-
## License
[MIT](LICENSE)
diff --git a/apps/docs/.vitepress/config.mts b/apps/docs/.vitepress/config.mts
index cc4caea4..68e71e41 100644
--- a/apps/docs/.vitepress/config.mts
+++ b/apps/docs/.vitepress/config.mts
@@ -32,6 +32,8 @@ export default defineConfig({
{ text: "Configuration", link: "/guide/configuration" },
{ text: "Database", link: "/guide/database" },
{ text: "Deployment", link: "/guide/deployment" },
+ { text: "Developer guide", link: "/guide/developer" },
+ { text: "Translation guide", link: "/guide/translations" },
],
},
{
diff --git a/apps/docs/guide/developer.md b/apps/docs/guide/developer.md
new file mode 100644
index 00000000..d7b0f8a6
--- /dev/null
+++ b/apps/docs/guide/developer.md
@@ -0,0 +1,204 @@
+# Developer guide
+
+How to set up a local development environment and contribute code to Stirling Image.
+
+## Prerequisites
+
+- [Node.js](https://nodejs.org/) 22+
+- [pnpm](https://pnpm.io/) 9+ (`corepack enable && corepack prepare pnpm@latest --activate`)
+- [Docker](https://www.docker.com/) (for container builds and AI features)
+- Git
+
+Python 3.10+ is only needed if you are working on the AI/ML sidecar (background removal, upscaling, OCR).
+
+## Setup
+
+```bash
+git clone https://github.com/siddharthksah/Stirling-Image.git
+cd Stirling-Image
+pnpm install
+pnpm dev
+```
+
+This starts two dev servers:
+
+| Service | URL | Notes |
+|----------|--------------------------|------------------------------------|
+| Frontend | http://localhost:1349 | Vite dev server, proxies /api |
+| Backend | http://localhost:13490 | Fastify API (accessed via proxy) |
+
+Open http://localhost:1349 in your browser. Login with `admin` / `admin`. You will be prompted to change the password on first login.
+
+## Project structure
+
+```
+apps/
+ api/ Fastify backend
+ web/ Vite + React frontend
+ docs/ VitePress documentation (this site)
+packages/
+ shared/ Constants, types, i18n strings
+ image-engine/ Sharp-based image operations
+ ai/ Python sidecar bridge for ML models
+tests/
+ unit/ Vitest unit tests
+ integration/ Vitest integration tests (full API)
+ e2e/ Playwright end-to-end specs
+ fixtures/ Small test images
+```
+
+## Commands
+
+```bash
+pnpm dev # start frontend + backend
+pnpm build # build all workspaces
+pnpm typecheck # TypeScript check across monorepo
+pnpm lint # Biome lint + format check
+pnpm lint:fix # auto-fix lint + format
+pnpm test # unit + integration tests
+pnpm test:unit # unit tests only
+pnpm test:integration # integration tests only
+pnpm test:e2e # Playwright e2e tests
+pnpm test:coverage # tests with coverage report
+```
+
+## Code conventions
+
+- Double quotes, semicolons, 2-space indentation (enforced by Biome)
+- ES modules in all workspaces
+- [Conventional commits](https://www.conventionalcommits.org/) for semantic-release
+- Zod for all API input validation
+- No modifications to Biome, TypeScript, or editor config files. Fix the code, not the linter.
+
+## Database
+
+SQLite via Drizzle ORM. The database file lives at `./data/stirling.db` by default.
+
+```bash
+cd apps/api
+npx drizzle-kit generate # generate a migration from schema changes
+npx drizzle-kit migrate # apply pending migrations
+```
+
+Schema is defined in `apps/api/src/db/schema.ts`. Tables: users, sessions, settings, jobs, apiKeys, pipelines, teams, userFiles.
+
+## Adding a new tool
+
+Every tool follows the same pattern. Here is a minimal example.
+
+### 1. Backend route
+
+Create `apps/api/src/routes/tools/my-tool.ts`:
+
+```ts
+import { z } from "zod";
+import type { FastifyInstance } from "fastify";
+import { createToolRoute } from "../tool-factory.js";
+
+const settingsSchema = z.object({
+ intensity: z.number().min(0).max(100).default(50),
+});
+
+export function registerMyTool(app: FastifyInstance) {
+ createToolRoute(app, {
+ toolId: "my-tool",
+ settingsSchema,
+ async process(inputBuffer, settings, filename) {
+ // Use sharp or other libraries to process the image
+ const sharp = (await import("sharp")).default;
+ const result = await sharp(inputBuffer)
+ // ... your processing logic
+ .toBuffer();
+
+ return {
+ buffer: result,
+ filename: filename.replace(/\.[^.]+$/, ".png"),
+ contentType: "image/png",
+ };
+ },
+ });
+}
+```
+
+Then register it in `apps/api/src/routes/tools/index.ts`.
+
+### 2. Frontend settings component
+
+Create `apps/web/src/components/tools/my-tool-settings.tsx`:
+
+```tsx
+import { useState } from "react";
+import { useToolProcessor } from "@/hooks/use-tool-processor";
+import { useFileStore } from "@/stores/file-store";
+
+export function MyToolSettings() {
+ const { files } = useFileStore();
+ const { processFiles, processing, error, downloadUrl } =
+ useToolProcessor("my-tool");
+
+ const [intensity, setIntensity] = useState(50);
+
+ const handleProcess = () => {
+ processFiles(files, { intensity });
+ };
+
+ return (
+
+ {/* your controls here */}
+
+
+ );
+}
+```
+
+Then add the route and component to the tool registry in the frontend.
+
+### 3. i18n entry
+
+Add to `packages/shared/src/i18n/en.ts`:
+
+```ts
+"my-tool": {
+ name: "My Tool",
+ description: "Short description of what this tool does",
+},
+```
+
+### 4. Tests
+
+Add a `data-testid` attribute to your action button (as shown above) so e2e tests can target it reliably.
+
+## Docker builds
+
+Build the full production image locally:
+
+```bash
+docker build -f docker/Dockerfile -t stirling-image:latest .
+```
+
+Use BuildKit cache mounts for faster rebuilds:
+
+```bash
+DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile -t stirling-image:latest .
+```
+
+## Environment variables
+
+See the [Configuration guide](/guide/configuration) for the full list. Key ones for development:
+
+| Variable | Default | Description |
+|-----------------------------|-----------|------------------------------------------------|
+| `AUTH_ENABLED` | `true` | Enable/disable authentication |
+| `DEFAULT_USERNAME` | `admin` | Default admin username |
+| `DEFAULT_PASSWORD` | `admin` | Default admin password |
+| `SKIP_MUST_CHANGE_PASSWORD` | `false` | Skip forced password change (CI/dev only) |
+| `RATE_LIMIT_PER_MIN` | `100` | API rate limit per minute |
+| `MAX_UPLOAD_SIZE_MB` | `100` | Maximum upload size in MB |
diff --git a/apps/docs/guide/translations.md b/apps/docs/guide/translations.md
new file mode 100644
index 00000000..1578a7ae
--- /dev/null
+++ b/apps/docs/guide/translations.md
@@ -0,0 +1,103 @@
+# Translation guide
+
+Stirling Image ships with English by default. The i18n system is designed so adding a new language is straightforward. This page walks you through the process.
+
+## How translations work
+
+All UI strings live in `packages/shared/src/i18n/`. The reference file is `en.ts`, which exports a typed object with every string the app uses. Other languages are separate files (e.g., `de.ts`, `fr.ts`) that export the same shape.
+
+The `TranslationKeys` type is derived from the English file, so TypeScript will catch any missing keys in your translation.
+
+## Adding a new language
+
+### 1. Fork and branch
+
+Fork the repository and create a new branch from `main`:
+
+```bash
+git checkout -b feat/add-german-translations
+```
+
+### 2. Copy the reference file
+
+```bash
+cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/de.ts
+```
+
+### 3. Translate the strings
+
+Open `de.ts` and translate every string value. Keep the object structure and keys exactly the same. Only change the values.
+
+```ts
+// packages/shared/src/i18n/de.ts
+export const de = {
+ common: {
+ upload: "Vom Computer hochladen",
+ process: "Verarbeiten",
+ download: "Herunterladen",
+ cancel: "Abbrechen",
+ // ... translate all entries
+ },
+ tools: {
+ resize: {
+ name: "Größe ändern",
+ description: "Größe nach Pixeln, Prozent oder Social-Media-Vorgaben ändern",
+ },
+ // ... translate all tool entries
+ },
+ // ... translate all sections: settings, auth, pipeline, nav
+} as const;
+```
+
+Things to keep in mind:
+
+- Preserve any interpolation placeholders if they exist in the future (e.g., `{count}`, `{filename}`).
+- Do not translate object keys, only values.
+- Keep the `as const` assertion at the end.
+
+### 4. Export the new language
+
+Edit `packages/shared/src/i18n/index.ts` to include your language:
+
+```ts
+export type { TranslationKeys } from "./en.js";
+export { en } from "./en.js";
+export { de } from "./de.js";
+```
+
+### 5. Register in the frontend
+
+The frontend needs to know about the new locale. Add it to the locale selector so users can switch languages. The exact location depends on how the locale switcher is implemented at the time of your contribution. Search for where `en` is referenced in the frontend and add your language alongside it.
+
+### 6. Test it
+
+```bash
+pnpm typecheck # will catch missing or mistyped keys
+pnpm dev # manually verify strings appear correctly
+```
+
+TypeScript is your safety net here. If your translation file is missing a key or has the wrong structure, `pnpm typecheck` will fail with a clear error.
+
+### 7. Submit a PR
+
+Push your branch and open a pull request. In the PR description, list which sections you translated and any strings you intentionally left in English (technical terms, proper nouns, etc.).
+
+## Strings that don't need translation
+
+Some strings are the same across languages: tool names that are English loanwords, technical terms, abbreviations. If a string is identical in your language, just leave it as the English value. No special configuration is needed.
+
+## Adding new translation keys
+
+If you are adding a new feature that needs new UI strings:
+
+1. Add the new keys to `packages/shared/src/i18n/en.ts` first. This is the reference file.
+2. Add translations to any other language files you can. If you can't translate them, leave the English value as a placeholder and note it in your PR.
+3. Run `pnpm typecheck` to make sure all language files still satisfy the `TranslationKeys` type.
+
+## File reference
+
+| File | Purpose |
+|------|---------|
+| `packages/shared/src/i18n/en.ts` | English strings (reference locale) |
+| `packages/shared/src/i18n/index.ts` | Exports all locales and the `TranslationKeys` type |
+| `packages/shared/src/constants.ts` | Tool registry (names/descriptions also live here) |