# Wishpad: repository and deployment specification

Документ фиксирует решения по лицензии исходного кода, хранению закрытого репозитория и deploy pipeline.

## 1. Статус Документа

Документ является частью технической спецификации Wishpad.

Связанные документы:

- `docs/SPEC.md` - основная продуктово-техническая спецификация;
- `docs/DOCUMENTATION_WORKFLOW.md` - правила ведения документации;
- `docs/CHANGELOG.md` - release notes.

## 2. Лицензия Исходного Кода

Исходный код Wishpad является закрытым.

Для проекта используется модель:

- proprietary software;
- all rights reserved;
- без open-source лицензии для исходного кода приложения.

В репозитории не создаётся `LICENSE` с open-source лицензией.

Для явной фиксации закрытого статуса проекта в корне репозитория используется `LICENSE.md` с proprietary license notice.

В `README.md` нужно добавить короткий license notice:

```md
## License

This project is proprietary software. All rights reserved.
You may not copy, modify, distribute, sublicense, or use this source code without explicit written permission.
```

Лицензии сторонних зависимостей должны проверяться отдельно перед production release.

## 3. Хранилище Кода

Выбранное решение для MVP:

- self-hosted Gitea;
- приватные репозитории;
- репозитории размещаются на инфраструктуре, контролируемой владельцем проекта;
- внешний GitHub/GitLab/Bitbucket не используется как основной источник кода.

Причины выбора Gitea:

- бесплатная и open-source;
- поддерживает приватные репозитории;
- поддерживает pull requests, issues, packages и CI/CD через Gitea Actions;
- легче self-hosted GitLab;
- снижает риск блокировок внешних SaaS-платформ;
- достаточно проста для solo/small-team разработки.

Canonical repositories:

| Repository | Назначение |
| --- | --- |
| `wishpad-docs` | product, UI, API, DB, deployment documentation, OpenAPI contract, brand assets |
| `wishpad-backend` | Laravel API backend, migrations, queue jobs, mail templates, backend/admin integration and backend tests |
| `wishpad-frontend` | Vue 3 + Vite frontend, admin frontend, Capacitor mobile layer, frontend tests and NPM tooling |

Legacy/bootstrap repository:

| Repository | Статус |
| --- | --- |
| `wishpad` | initial bootstrap repository; kept for history until docs/code repositories fully replace it |

Рекомендуемые настройки code repositories:

- репозиторий приватный;
- основная ветка: `main`;
- интеграционная ветка: `dev`;
- прямой push в `main` можно разрешить на раннем solo-этапе;
- при появлении команды включить protected branch и merge через pull request;
- доступ выдавать только нужным пользователям;
- включить 2FA для пользователей Gitea, если доступно в выбранной конфигурации;
- секреты не хранить в git.

### 3.1. Доступ К Репозиторию

Canonical Gitea URL:

```text
https://gitea.wishpad.me/
```

Repository:

```text
wishpad-admin/wishpad-docs
wishpad-admin/wishpad-backend
wishpad-admin/wishpad-frontend
```

HTTPS remote:

```text
https://gitea.wishpad.me/wishpad-admin/wishpad-docs.git
https://gitea.wishpad.me/wishpad-admin/wishpad-backend.git
https://gitea.wishpad.me/wishpad-admin/wishpad-frontend.git
```

SSH remote:

```text
ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-docs.git
ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-backend.git
ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-frontend.git
```

Для локальной разработки предпочтителен SSH remote после добавления личного public key в Gitea account settings.
HTTPS remote допустим для серверных maintenance scripts и первичной настройки, но пароль или token не должны попадать в shell history, git remote URL или logs.

Локальные административные доступы для текущего рабочего места хранятся вне git:

```text
.secrets/deploy.env
.secrets/gitea.env
.secrets/ssh/
```

Server-side секреты хранятся на VPS:

```text
/etc/wishpad/secrets/
```

Эти файлы нельзя копировать в репозиторий, issue, pull request, workflow logs или frontend bundle.

### 3.2. Работа С Ветками

Базовый цикл разработки:

```bash
git clone ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-backend.git
git clone ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-frontend.git
git clone ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-docs.git

cd wishpad-backend
git switch dev
```

Правила веток:

- `dev` - основная ветка разработки и интеграционной проверки на `https://dev.wishpad.me`;
- `main` - production branch для `https://wishpad.me`;
- обычные изменения сначала попадают в `dev`;
- перенос в production делается merge из `dev` в `main`;
- hotfix можно делать от `main`, затем обязательно переносить обратно в `dev`;
- секреты и machine-specific файлы не коммитятся.

Рекомендуемый promote flow:

```bash
git switch dev
git pull
# implement and commit changes
git push origin dev
# после проверки dev окружения:
git switch main
git pull
git merge --no-ff dev
git push origin main
```

На раннем solo-этапе допускается прямой push в `main`, но он сразу запускает production deploy workflow.
Перед production push нужно убедиться, что dev environment уже проверен.

### 3.3. Локальные И Серверные Remotes

В developer checkout можно использовать SSH remote:

```bash
git remote set-url origin ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-backend.git
```

В server-side checkout допускается internal HTTP remote:

```text
http://127.0.0.1:3000/wishpad-admin/wishpad-backend.git
http://127.0.0.1:3000/wishpad-admin/wishpad-frontend.git
http://127.0.0.1:3000/wishpad-admin/wishpad-docs.git
```

Внутри Gitea runner containers используется Docker-network remote:

```text
http://wishpad-gitea:3000/wishpad-admin/wishpad-backend.git
http://wishpad-gitea:3000/wishpad-admin/wishpad-frontend.git
http://wishpad-gitea:3000/wishpad-admin/wishpad-docs.git
```

Deploy scripts должны выбирать правильный remote по контексту выполнения и не сохранять credentials в remote URL.

## 4. CI/CD

Для MVP используется встроенный CI/CD Gitea:

- Gitea Actions;
- Gitea Runner;
- workflow files в репозитории;
- Docker-based job execution там, где это возможно.

Отдельная deploy-платформа для MVP не обязательна.

Coolify и Dokploy остаются future option, если позже потребуется:

- dashboard для deploy;
- удобное управление env-переменными;
- управление несколькими окружениями;
- визуальные логи деплоев;
- rollback из UI;
- более удобное управление несколькими приложениями и базами.

## 5. MVP Deploy Flow

Целевая схема:

```text
Developer
  -> git push
  -> self-hosted Gitea
  -> Gitea Actions workflow
  -> Gitea Runner
  -> VPS
  -> Docker Compose
  -> Wishpad production services
```

Ожидаемый production flow:

1. Разработчик делает push в `main`.
2. Gitea Actions запускает workflow.
3. Workflow собирает frontend assets.
4. Workflow проверяет backend/frontend, если соответствующие проверки уже настроены.
5. Workflow доставляет изменения на VPS.
6. На VPS выполняется `docker compose` deploy.
7. Laravel migrations выполняются после доставки новой версии.
8. Queue worker и scheduler перезапускаются или обновляются вместе с Docker services.
9. Nginx отдаёт frontend static assets и проксирует API на Laravel backend.

Точный способ доставки на VPS фиксируется на этапе реализации:

- через SSH-команды из Gitea Actions;
- или через pull на VPS;
- или через сборку и публикацию Docker images в Gitea package/container registry.

Для MVP предпочтителен самый простой вариант:

- Gitea Actions подключается к VPS по SSH;
- на VPS выполняется обновление исходников или образов;
- затем запускаются `docker compose` команды.

## 5.1. Домены Инфраструктуры

DNS zone для `wishpad.me` хранится в `docs/wishpad.me.zone.txt`.

Для MVP используются следующие публичные имена:

| Host | Назначение | Целевой VPS |
| --- | --- | --- |
| `wishpad.me` | production web/API | `168.222.202.29` |
| `www.wishpad.me` | алиас production, редирект на `wishpad.me` | `168.222.202.29` |
| `dev.wishpad.me` | development environment для ветки `dev` | `168.222.202.29` |
| `docs.wishpad.me` | documentation engine / Scalar OpenAPI viewer | `168.222.202.29` |
| `gitea.wishpad.me` | self-hosted Gitea UI и Git HTTP remote | `168.222.202.29` |

Gitea не должен жить в подпути production-домена. После появления `gitea.wishpad.me` canonical Gitea URL:

```text
https://gitea.wishpad.me/
```

Git over SSH для Gitea использует отдельный порт:

```text
ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-backend.git
ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-frontend.git
ssh://git@gitea.wishpad.me:2222/wishpad-admin/wishpad-docs.git
```

Production и dev окружения обслуживаются тем же VPS, но должны быть разделены на уровне:

- Git branch;
- Gitea Actions runner label;
- application checkout directory;
- Docker Compose project name;
- Docker network;
- database volume;
- environment file;
- public domain;
- backup namespace.

## 5.2. Branch-To-Environment Mapping

Для MVP фиксируется два server-side окружения:

| Branch | Environment | Public URL | Deploy runner label | VPS checkout |
| --- | --- | --- | --- | --- |
| `dev` in `wishpad-backend` / `wishpad-frontend` | development | `https://dev.wishpad.me` | `wishpad-dev` | `/var/www/wishpad-dev/backend`, `/var/www/wishpad-dev/frontend` |
| `main` in `wishpad-backend` / `wishpad-frontend` | production | `https://wishpad.me` | `wishpad-production` | `/var/www/wishpad-production/backend`, `/var/www/wishpad-production/frontend` |
| `main` in `wishpad-docs` | documentation | `http://docs.wishpad.me` until TLS propagates, then `https://docs.wishpad.me` | `wishpad-docs` | `/var/www/wishpad-docs` |

Правила:

- push в `dev` запускает deploy только development environment;
- push в `main` запускает deploy только production environment;
- production deploy не должен использовать dev `.env`, dev database volume или dev compose project;
- development deploy не должен использовать production `.env`, production database volume или production compose project;
- ручной deploy допускается только как emergency/maintenance operation и должен повторять те же команды, что workflow;
- `main` считается production branch;
- `dev` считается интеграционной веткой для разработки и проверки изменений до production.

На раннем solo-этапе допустим прямой push в `main` и `dev`.
Когда появится команда или внешний доступ, `main` нужно защитить и переводить изменения из `dev` через pull request.

## 5.3. Gitea Actions Runners

Для окружений используются отдельные Gitea Actions runners или отдельные runner containers с разными labels.

Минимальные runner labels:

```text
wishpad-dev
wishpad-production
wishpad-docs
```

Рекомендуемая структура runner containers на VPS:

```text
/opt/wishpad/gitea-runner-dev/docker-compose.yml
/opt/wishpad/gitea-runner-production/docker-compose.yml
/opt/wishpad/gitea-runner-docs/docker-compose.yml
```

Каждый runner:

- регистрируется в той же Gitea instance;
- использует Docker socket только если workflow реально собирает Docker images или запускает Docker-based jobs;
- имеет отдельный container name;
- имеет отдельный data volume;
- объявляет только свой environment label;
- не должен выполнять jobs другого окружения.

Пример labels:

```text
GITEA_RUNNER_LABELS=wishpad-dev:host
GITEA_RUNNER_LABELS=wishpad-production:host
```

Если workflow использует Docker job images, labels можно расширить, например:

```text
wishpad-dev:host,ubuntu-latest:docker://node:20-bookworm
wishpad-production:host,ubuntu-latest:docker://node:20-bookworm
```

Workflow для production должен явно использовать `runs-on: wishpad-production`.
Workflow для dev должен явно использовать `runs-on: wishpad-dev`.
Workflow для documentation должен явно использовать `runs-on: wishpad-docs`.

Текущие workflow files:

```text
wishpad-backend/.gitea/workflows/deploy-dev.yml
wishpad-backend/.gitea/workflows/deploy-production.yml
wishpad-frontend/.gitea/workflows/deploy-dev.yml
wishpad-frontend/.gitea/workflows/deploy-production.yml
wishpad-docs/.gitea/workflows/deploy-docs.yml
```

Smoke-test workflow runs подтверждают, что runners разделены корректно:

```text
wishpad-backend:dev      305af68 Add environment deploy workflows
wishpad-backend:main     11fa942 Add environment deploy workflows
wishpad-frontend:dev     eab6a7b Add environment deploy workflows
wishpad-frontend:main    ecf49ae Add environment deploy workflows
wishpad-docs:main        10ac6df Add documentation deploy workflow
```

Первые failed runs после создания workflows допустимы как исторический след настройки:
они падали из-за `127.0.0.1` remote внутри runner container.
Актуальные deploy scripts используют context-aware remote и smoke runs завершаются успешно.

## 5.4. VPS Directory Layout

Базовая структура инфраструктуры на VPS:

```text
/opt/wishpad/
  gitea/
  gitea-runner-dev/
  gitea-runner-production/
  gitea-runner-docs/
  deploy/
    dev/
    production/
    docs/

/var/www/
  wishpad-dev/
    backend/
    frontend/
  wishpad-production/
    backend/
    frontend/
  wishpad-docs/
  wishpad-docs-site/

/var/lib/wishpad/
  gitea/
  gitea-runner-dev/
  gitea-runner-production/
  gitea-runner-docs/
  dev/
  production/

/var/backups/wishpad/
  gitea/
  dev/
  production/

/etc/wishpad/
  secrets/
    gitea.env
    gitea-runner-dev.env
    gitea-runner-production.env
    dev.env
    production.env
```

Назначение:

- `/opt/wishpad/*` - infrastructure compose files, deploy scripts and operational config;
- `/var/www/wishpad-dev/backend` - checkout `wishpad-backend` ветки `dev`;
- `/var/www/wishpad-dev/frontend` - checkout `wishpad-frontend` ветки `dev`;
- `/var/www/wishpad-production/backend` - checkout `wishpad-backend` ветки `main`;
- `/var/www/wishpad-production/frontend` - checkout `wishpad-frontend` ветки `main`;
- `/var/www/wishpad-docs` - checkout `wishpad-docs` ветки `main`;
- `/var/www/wishpad-docs-site` - generated static documentation site served by Nginx;
- `/var/lib/wishpad/*` - persistent Docker volumes and runtime state;
- `/var/backups/wishpad/*` - environment-specific backups;
- `/etc/wishpad/secrets/*` - server-side secrets, never committed to git.

## 5.5. Application Docker Compose Environments

Wishpad application deploy uses Docker Compose.

Dev и production должны использовать разные compose project names:

```text
COMPOSE_PROJECT_NAME=wishpad_dev
COMPOSE_PROJECT_NAME=wishpad_production
```

Ожидаемые production services описаны в `docs/SPEC.md`:

- `backend`;
- `postgres`;
- `nginx`;
- `queue`;
- `scheduler`.

`frontend` в production не обязан быть постоянно запущенным, потому что Vue/Vite frontend собирается в static assets.

Для development environment на VPS допускается тот же production-like режим:

- frontend собирается в static assets;
- Nginx отдаёт frontend и проксирует `/api`;
- backend, queue, scheduler и PostgreSQL живут в отдельных containers.

Это не заменяет local development environment.
`dev.wishpad.me` нужен для интеграционной проверки server-side deploy.

Окружения должны иметь отдельные PostgreSQL volumes.
Общий PostgreSQL container для dev и production в MVP не используется, чтобы снизить риск пересечения данных.

## 5.6. Deploy Workflow Responsibilities

Dev workflow:

1. Trigger: push в `dev` in `wishpad-backend` or `wishpad-frontend`.
2. Runner: `wishpad-dev`.
3. Checkout branch: `dev`.
4. Target directory: `/var/www/wishpad-dev`.
5. Env file: `/etc/wishpad/secrets/dev.env`.
6. Compose project: `wishpad_dev`.
7. Public URL: `https://dev.wishpad.me`.

Production workflow:

1. Trigger: push в `main` in `wishpad-backend` or `wishpad-frontend`.
2. Runner: `wishpad-production`.
3. Checkout branch: `main`.
4. Target directory: `/var/www/wishpad-production`.
5. Env file: `/etc/wishpad/secrets/production.env`.
6. Compose project: `wishpad_production`.
7. Public URL: `https://wishpad.me`.

Both workflows:

- fetch or pull both backend and frontend target branches;
- install/build only what is required by the selected deploy strategy;
- run Docker Compose from the target checkout or from `/opt/wishpad/deploy/<env>`;
- run Laravel migrations after services are updated;
- restart queue and scheduler containers;
- fail fast if required env files are missing;
- avoid printing secrets;
- leave enough logs for diagnosis.

Documentation workflow:

1. Trigger: push в `main` in `wishpad-docs`.
2. Runner: `wishpad-docs`.
3. Checkout branch: `main`.
4. Target repository checkout: `/var/www/wishpad-docs`.
5. Generated static site: `/var/www/wishpad-docs-site`.
6. Public URL: `https://docs.wishpad.me`.

Canonical documentation project config: `scalar.config.json`.

`scalar.config.json` follows Scalar Docs 2.0 structure and maps product Markdown specs plus `docs/OPENAPI.yaml` into one documentation navigation tree.

The current VPS deployment remains self-hosted:

- the documentation site root is a documentation hub with links to product and technical specifications;
- Scalar API Reference is available at `/openapi.html`;
- `docs/OPENAPI.yaml` is served as `/openapi.yaml`.

Future option: publish the same `scalar.config.json` project through Scalar Docs Platform if paid custom-domain hosting becomes useful.

## 5.7. Nginx Routing

Nginx on the host terminates TLS and routes public domains.

Required host-level virtual hosts:

| Host | Upstream |
| --- | --- |
| `gitea.wishpad.me` | `127.0.0.1:3000` |
| `dev.wishpad.me` | dev Wishpad Nginx/app upstream |
| `docs.wishpad.me` | static Scalar documentation site in `/var/www/wishpad-docs-site` |
| `wishpad.me` | production Wishpad Nginx/app upstream |
| `www.wishpad.me` | redirect to `https://wishpad.me` |

TLS certificates are managed by Certbot on the VPS.

Gitea uses host-level Nginx and is not part of application deploy.
Wishpad app Nginx can run inside each environment compose project; host Nginx proxies to its exposed localhost port.

Recommended local host ports:

```text
dev app nginx:        127.0.0.1:8081
production app nginx: 127.0.0.1:8080
gitea:                127.0.0.1:3000
```

These ports are implementation details and must not be exposed publicly except through host Nginx.

## 6. Секреты И Доступы

Секреты хранятся вне git.

Нужно предусмотреть:

- production `.env` на VPS;
- SSH key для deploy;
- Gitea Actions secrets для доступа к VPS;
- отдельного deploy-пользователя на VPS;
- ограниченные права deploy-пользователя;
- запрет вывода секретов в CI logs.

Текущая локальная структура секретов:

```text
.secrets/
  deploy.env      # SSH/VPS connection metadata
  gitea.env       # local Gitea admin metadata and generated secrets
  ssh/            # private/public SSH keys for VPS access
```

Текущая server-side структура секретов:

```text
/etc/wishpad/secrets/
  gitea.env
  gitea-runner-dev.env
  gitea-runner-production.env
  dev.env
  production.env
```

Назначение:

- `gitea.env` - Gitea database password, internal tokens, admin bootstrap credentials;
- `gitea-runner-*.env` - one-time runner registration token snapshots;
- `dev.env` - application env для `dev.wishpad.me`;
- `production.env` - application env для `wishpad.me`.

Application env files уже созданы как placeholders для будущего Laravel/Vue deploy.
SMTP credentials пока должны быть заполнены вручную перед включением email sending.

Секреты SMTP.bz, PostgreSQL, `APP_KEY`, deploy SSH key и любые production tokens не должны попадать:

- в git;
- в frontend bundle;
- в публичные логи;
- в OpenAPI examples;
- в issue/PR тексты.

При ротации Gitea admin password нужно обновить:

- `.secrets/gitea.env` на рабочей машине;
- `/etc/wishpad/secrets/gitea.env` на VPS;
- deploy scripts или automation, если они читают этот пароль для server-side fetch.

При ротации VPS SSH key нужно обновить:

- public key в `/root/.ssh/authorized_keys` или у будущего deploy user;
- `VPS_SSH_KEY` в `.secrets/deploy.env`;
- локальную памятку в `.secrets/README.md`, если путь ключа изменился.

## 7. Среды

Для MVP обязательны:

- local development;
- production.

Staging не обязателен для MVP, но архитектурно не должен блокироваться.

Future option:

- staging environment на отдельном домене или поддомене;
- отдельная БД staging;
- отдельные env secrets;
- ручной promote flow из staging в production.

## 8. Backup

Backup относится к production-инфраструктуре и описан в `docs/SPEC.md`.

Для Gitea дополнительно нужно предусмотреть backup:

- Git repositories;
- Gitea database;
- Gitea config;
- uploaded attachments/packages, если они будут использоваться.

Gitea backup должен быть отделён от backup PostgreSQL приложения Wishpad.

## 9. Future Options

В будущем можно рассмотреть:

- Coolify как self-hosted deploy dashboard;
- Dokploy как self-hosted deploy dashboard;
- Gitea package/container registry для Docker images;
- отдельный staging server;
- protected branches;
- обязательные pull requests;
- обязательные CI checks перед merge;
- автоматический backup Gitea;
- зеркалирование приватного репозитория на второй self-hosted Git server.
