# HarborForge Backend The core REST API for HarborForge — an Agent/人类协同任务管理平台 (Agent/Human collaborative task-management platform). Part of the [HarborForge](../README.md) platform. - **Role:** core REST API — users, projects, tasks, milestones, proposals, RBAC, webhooks, worklogs, notifications, monitor telemetry. - **Stack:** Python 3.11 · FastAPI · SQLAlchemy · MySQL - **Port:** `8000` The service reads its database configuration from the AbstractWizard config volume (falling back to env/defaults) and authenticates requests with JWT (HS256) signed by `SECRET_KEY`. ## Run / Build ### Docker ```bash docker build -t harborforge-backend . docker run -p 8000:8000 \ -e SECRET_KEY="$(openssl rand -hex 32)" \ -v /path/to/config:/config \ harborforge-backend ``` ### Local (uvicorn) ```bash pip install -r requirements.txt export SECRET_KEY="$(openssl rand -hex 32)" uvicorn app.main:app --host 0.0.0.0 --port 8000 ``` On startup the app creates/migrates the schema, runs AbstractWizard initialization (admin user, default project, default roles), and starts a background monitor-polling thread. ## Configuration Environment variables (also loadable from a `.env` file): | Variable | Default | Description | |----------|---------|-------------| | `SECRET_KEY` | *(none — must be set)* | JWT signing key (HS256). The server **refuses to start** with a weak/default/short value. | | `DATABASE_URL` | `mysql+pymysql://harborforge:harborforge_pass@mysql:3306/harborforge` | Fallback DB URL when the wizard config volume is absent. | | `ALGORITHM` | `HS256` | JWT algorithm. | | `ACCESS_TOKEN_EXPIRE_MINUTES` | `30` | Access-token lifetime. | | `LOG_LEVEL` | `INFO` | Log level. | | `CONFIG_DIR` | `/config` | AbstractWizard config volume directory. | | `CONFIG_FILE` | `harborforge.json` | Config file name within `CONFIG_DIR`. | Database resolution order: **wizard config volume** (`$CONFIG_DIR/$CONFIG_FILE` → `database` block) → `DATABASE_URL` env → built-in default. ## Security The current code enforces the following security posture. These are operational requirements, not optional hardening. ### Mandatory strong `SECRET_KEY` `app/core/config.py` validates `SECRET_KEY` at import time and **raises and refuses to start** if the value is empty, shorter than 32 characters, or a known default/placeholder (e.g. `change-me-in-production`, `secret`, `changeme`). Operators **must** provide a strong random key: ```bash openssl rand -hex 32 ``` A weak signing key allows JWT forgery and full authentication bypass, so this check is intentionally fatal. ### API-key management is admin-only and masked The `/api-keys` endpoints (`POST`, `GET`, `DELETE /api-keys/{id}`) all require a global admin (`require_admin`). Listing **never returns the full secret** — keys are masked to a short prefix/suffix (e.g. `abc123…9f`). The full key is only returned once, on creation. ### Webhooks router is admin-only with SSRF protection The entire `/webhooks` router is mounted with `dependencies=[Depends(require_admin)]`, so every webhook endpoint (create/list/get/update/delete/logs/retry) requires a global admin. Webhook delivery (`app/services/webhook.py`) validates the target URL before sending: - Only `http`/`https` schemes are allowed. - The host is DNS-resolved and **every** resolved address is rejected if it is private, loopback, link-local, multicast, reserved, or unspecified (SSRF protection). - HTTP redirects are disabled (`follow_redirects=False`). ### Project role hierarchy enforcement `check_project_role` in `app/api/rbac.py` enforces a real, ordered role hierarchy rather than a flat membership check: ``` guest(0) < viewer(1) < member(2) < dev(3) < mgr(4) < admin(5) ``` A caller below the required rank is denied with `403`, and any unknown role on either side is denied by default. Global admins bypass project-level checks. ### Authentication on previously open endpoints The following endpoints now require an authenticated caller (JWT bearer token **or** `X-API-Key`) and enforce ownership/permission: - `DELETE /milestones/{id}` — requires milestone-edit permission. - `POST /worklogs` — worklogs are always attributed to the caller; only admins may log time for another user. - `DELETE /worklogs/{id}` — caller-scoped; non-admins cannot delete another user's worklog. - `POST /tasks/{task_code}/assign` and `POST /tasks/batch/assign`. - `GET /activity`. - `GET /export/tasks`. ## Authentication - `POST /auth/token` — OAuth2 password grant; returns a JWT bearer token. - Authenticated requests send `Authorization: Bearer ` **or** `X-API-Key: ` (API keys map to a user and are created by admins). - `GET /auth/me` — current user. - `GET /auth/me/permissions`, `GET /auth/me/apikey-permissions` — permission introspection. ## Key API Areas | Area | Prefix / Routes | Notes | |------|-----------------|-------| | Auth | `/auth/*` | token, current user, permission introspection | | Users | `/users` | registration, list/detail/update (list & mutate are admin-only) | | Projects | `/projects` | CRUD, members (`/projects/{id}/members`), worklog summary | | Project members | `/projects/{id}/members` | add/list/remove with role | | Milestones | `/projects/{id}/milestones`, `/milestones/{id}` | CRUD, items, progress | | Milestone actions | preflight / freeze / start / close | lifecycle transitions | | Tasks | `/tasks` | CRUD, transition, take, assign, batch transition/assign, tags, search | | Comments | `/comments`, `/tasks/{id}/comments` | CRUD | | Proposals | `/projects/{code}/proposals` | propose / accept / reject / reopen (legacy `/proposes`) | | Essentials | proposal essentials | feature/improvement/refactor items | | Meetings | `/meetings` | create/list/detail/update/delete/attend | | Roles & RBAC | `/roles` | roles, permissions, role-permission assignment | | Webhooks | `/webhooks` | **admin-only**; CRUD, logs, retry (SSRF-guarded delivery) | | API keys | `/api-keys` | **admin-only**; create/list (masked)/revoke | | Worklogs | `/worklogs`, `/tasks/{id}/worklogs`, `/users/{id}/worklogs` | time tracking & summaries | | Notifications | `/notifications` | list, unread count, mark read / read-all | | Activity | `/activity` | activity log (authenticated) | | Export | `/export/tasks` | CSV export (authenticated) | | Calendar | `/calendar` | scheduling / time slots | | Monitor | `/monitor` | public overview, admin providers/servers, heartbeat telemetry | | Dashboard | `/dashboard/stats` | aggregate statistics | | System | `/health`, `/version`, `/config/status` | health, version, init status | ## Task Types | Type | 用途 | |------|------| | issue | 普通任务 | | story | 用户故事 | | test | 测试用例 | | resolution | 决议案(Agent 僵局提交)| ## CLI The legacy Python CLI (`cli.py`) has been retired. Use the Go-based `hf` CLI instead. See [HarborForge.Cli](../HarborForge.Cli/README.md) for installation and usage. ## Tech Stack - Python 3.11 + FastAPI - SQLAlchemy + MySQL (auto schema create/migrate on startup) - JWT (python-jose, HS256) + bcrypt password hashing - Docker