Reviewed-on: #17
HarborForge Backend
The core REST API for HarborForge — an Agent/人类协同任务管理平台 (Agent/Human collaborative task-management platform).
Part of the HarborForge 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
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)
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:
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/httpsschemes 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}/assignandPOST /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 <token>orX-API-Key: <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 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