This commit is contained in:
delete 2026-06-10 20:51:17 +03:00
commit b15b95781c
108 changed files with 14802 additions and 0 deletions

486
PRODUCT_SPEC.md Normal file
View file

@ -0,0 +1,486 @@
# Update Server - Product Specification
## 1. Goal
Build a small self-hosted update server for distributing application updates.
The system should allow an administrator to:
- create projects;
- upload update files for those projects;
- manage client API keys;
- configure which API keys can access which projects and which actions are allowed;
- group projects with tags;
- provide a simple web UI for administration;
- expose an HTTP API for client applications to check, fetch, and manage updates.
Primary implementation language: Go.
## 2. Product idea in one sentence
An admin uploads versioned update artifacts into projects, and client applications use API keys to securely discover and download only the updates they are allowed to access.
## 3. Main entities
### 3.1 Project
A project represents one distributable application, file, package, or update stream.
Suggested fields:
- `id`
- `name`
- `slug`
- `description`
- `created_at`
- `updated_at`
- `is_active`
Notes:
- In MVP, one project can simply correspond to one app or one downloadable file family.
- Later this can be extended with channels like `stable`, `beta`, `nightly`.
- Projects should support tags from the start.
### 3.2 Release / Update
A release is an uploaded update artifact belonging to a project.
Suggested fields:
- `id`
- `project_id`
- `version`
- `build`
- `filename`
- `storage_path`
- `checksum_sha256`
- `size_bytes`
- `content_type`
- `release_notes`
- `created_at`
- `uploaded_by_user_id`
- `is_active`
Optional future fields:
- `channel`
- `min_client_version`
- `rollout_percent`
- `metadata_json`
### 3.3 API Key
An API key is used by client software to authenticate against the update API.
Suggested fields:
- `id`
- `name`
- `key_prefix`
- `key_hash`
- `description`
- `scope_mode`
- `is_active`
- `expires_at`
- `created_at`
- `updated_at`
- `last_used_at`
Security note:
- store only a hash of the full API key, never the raw key;
- show the full key only once when created.
### 3.4 User
At least one admin user is needed for the web UI.
Suggested fields:
- `id`
- `email` or `login`
- `password_hash`
- `role`
- `is_active`
- `created_at`
- `updated_at`
- `last_login_at`
MVP note:
- start with one admin user;
- design schema so multiple users can be supported without a major rewrite.
### 3.5 Tag
A tag is a reusable label attached to projects and later used in access rules.
Examples:
- `windows`
- `beta`
- `internal`
- `customer-acme`
- `desktop`
Suggested fields:
- `id`
- `name`
- `slug`
- `description`
- `created_at`
Relations:
- one project can have many tags;
- one tag can be attached to many projects;
- API keys can reference tags in access rules.
## 4. Access control model
Each API key should have both action permissions and project scope.
### 4.1 Action permissions
Suggested permissions:
- `updates.read` - check available updates and download files
- `updates.upload` - upload releases
- `updates.delete` - delete releases
- `projects.read` - list accessible projects
- `projects.write` - create or edit projects
- `admin.read` - view admin-level metadata
- `admin.write` - manage API keys and access rules
For MVP, this can be simplified to:
- `can_download`
- `can_upload`
- `can_delete`
- `can_manage_projects`
### 4.2 Scope strategies
Each API key should support one access scope strategy:
- access to all projects;
- project white list;
- project black list;
- tag white list;
- tag black list.
Recommended v1 model:
- mode `all_projects`
- mode `project_allow_list`
- mode `project_deny_list`
- mode `tag_allow_list`
- mode `tag_deny_list`
Why:
- this supports both explicit project lists and future tag-based grouping;
- it avoids the complexity of mixing many overlapping rule types in one key;
- it keeps evaluation logic predictable and safer.
### 4.3 Access evaluation
Recommended effective access logic:
- `all_projects`: key can access all active projects;
- `project_allow_list`: key can access only linked projects;
- `project_deny_list`: key can access all active projects except linked projects;
- `tag_allow_list`: key can access projects that have at least one linked tag;
- `tag_deny_list`: key can access all active projects except projects that have one of the blocked tags.
Recommended v1 restriction:
- one API key uses exactly one scope strategy at a time.
This keeps the admin UI and permission checks much simpler.
## 5. Admin web UI
### 5.1 Pages for MVP
1. Login page
2. Dashboard
3. Projects list
4. Create/edit project page
5. Project detail page
6. Release upload page or upload block inside project detail
7. Tags list
8. Create/edit tag page
9. API keys list
10. Create API key page
11. API key detail/edit page
12. Users page or at least admin bootstrap settings page
### 5.2 Minimal UI capabilities
Projects:
- create project;
- edit project;
- archive or disable project;
- view releases for a project;
- upload new release;
- delete or disable release;
- attach and detach tags.
API keys:
- generate key;
- disable key;
- set expiration;
- select scope strategy;
- assign project white list or black list;
- assign tag white list or black list;
- assign action permissions;
- inspect last usage.
Tags:
- create tag;
- edit tag;
- delete unused tag;
- attach tags to projects;
- use tags in API key access rules.
Users:
- login/logout;
- change password;
- optionally create another admin/editor later.
## 6. Client API
The API should support both admin operations and client update retrieval.
### 6.1 Auth model
- web UI uses session or secure cookie auth;
- external clients use API key auth via header, for example:
- `Authorization: Bearer <api_key>`
- or `X-API-Key: <api_key>`
Recommendation:
- use `Authorization: Bearer <api_key>` for external API consistency.
### 6.2 Suggested client endpoints
#### Check accessible projects
- `GET /api/v1/projects`
Returns only projects accessible to the API key.
#### Check latest release for a project
- `GET /api/v1/projects/{projectSlug}/releases/latest`
Possible query parameters:
- `current_version`
- `channel`
- `platform`
- `arch`
#### List releases for a project
- `GET /api/v1/projects/{projectSlug}/releases`
#### Download release file
- `GET /api/v1/releases/{releaseID}/download`
Alternative:
- `GET /api/v1/projects/{projectSlug}/releases/{version}/download`
#### Get release metadata
- `GET /api/v1/releases/{releaseID}`
### 6.3 Suggested admin endpoints
- `POST /api/v1/admin/projects`
- `PATCH /api/v1/admin/projects/{id}`
- `DELETE /api/v1/admin/projects/{id}`
- `GET /api/v1/admin/tags`
- `POST /api/v1/admin/tags`
- `PATCH /api/v1/admin/tags/{id}`
- `DELETE /api/v1/admin/tags/{id}`
- `POST /api/v1/admin/projects/{id}/releases`
- `DELETE /api/v1/admin/releases/{id}`
- `GET /api/v1/admin/api-keys`
- `POST /api/v1/admin/api-keys`
- `PATCH /api/v1/admin/api-keys/{id}`
- `DELETE /api/v1/admin/api-keys/{id}`
- `GET /api/v1/admin/users`
- `POST /api/v1/admin/users`
## 7. Storage
### 7.1 Database recommendation
Recommended for this project: `SQLite`.
Reasoning:
- single file database;
- easy to ship inside one container;
- good fit for low-to-medium admin traffic;
- excellent support in Go;
- no need to run a separate DB service.
Important clarification:
- `SQLite` is likely what was meant by "MySQL Lite";
- `MySQL` itself is a separate server database and would add complexity.
### 7.2 File storage
Store uploaded release files on local disk in the container or in a mounted volume.
Suggested layout:
- `/data/db.sqlite`
- `/data/artifacts/{project_slug}/{version}/{filename}`
Important:
- use a mounted persistent volume in deployment, not only container-local ephemeral storage;
- keep uploaded files outside any public static web root;
- downloads should be authorized by the app, not exposed as open file URLs.
## 8. Roles and authentication
### 8.1 Web users
Suggested roles:
- `admin` - full access
- `editor` - can upload and manage projects, but not system settings
- `viewer` - read-only, optional future role
MVP:
- implement `admin` first;
- keep schema ready for extra roles later.
### 8.2 Initial bootstrap
At first startup:
- create admin user from environment variables;
- or provide a one-time bootstrap CLI command.
Recommended env vars:
- `ADMIN_EMAIL`
- `ADMIN_PASSWORD`
## 9. Security requirements
Because this server may be reachable from the public internet, security is a first-class requirement.
Core requirements:
- every admin page must require authenticated session access;
- every API endpoint must enforce permission checks;
- uploaded files must never be executed or unpacked on the server;
- artifacts must be stored outside the public web root;
- downloads must go through authorization checks, not open static directories;
- passwords must be hashed with a modern password hash;
- API keys must be hashed and shown only once;
- rate limiting should protect login and API key endpoints;
- request size limits and timeouts must be enabled;
- CSRF protection must be enabled for admin forms;
- path traversal and arbitrary file overwrite must be prevented;
- audit logs should record login attempts, uploads, deletions, and key usage;
- TLS should be terminated by a reverse proxy such as Caddy or Nginx;
- security headers should be enabled;
- dependencies should be kept updated.
Recommended additional protection:
- optionally restrict admin UI by IP allow-list or VPN;
- disable deleted or compromised API keys immediately;
- back up both SQLite DB and artifacts;
- never process uploaded files with shell commands;
- keep secrets in environment variables, not in repository files.
## 10. Non-functional requirements
- easy local development;
- deployable as one container;
- persistent storage via volume mount;
- secure API key handling;
- auditability of uploads and key usage;
- basic request logging;
- simple backup strategy for DB and artifacts.
## 11. MVP scope
The first usable version should include:
- admin login;
- project CRUD;
- project tags;
- release upload;
- release listing;
- API key generation and revocation;
- API key white-list or black-list by project;
- schema support for tag-based access rules;
- API endpoint to fetch latest release metadata;
- API endpoint to download release;
- SQLite database;
- local disk artifact storage;
- Docker deployment;
- reverse-proxy-ready setup for HTTPS.
## 12. Post-MVP ideas
- release channels (`stable`, `beta`);
- semantic version comparison;
- delta updates / patch files;
- signed manifests;
- download tokens with short TTL;
- release publishing workflow as draft/published;
- webhook notifications;
- S3-compatible artifact storage;
- rate limiting;
- audit log UI;
- per-platform binaries;
- automatic cleanup rules;
- client usage analytics.
## 13. Open product decisions
These should be decided before implementation starts:
1. Should clients always download the latest version, or should they first request metadata and decide themselves?
2. Do releases need channels like `stable` and `beta` from day one?
3. Should upload via API key be allowed in MVP, or only through the web admin UI?
4. Are deleted releases truly removed, or just soft-deleted/hidden?
5. Should one project contain only one file per version, or potentially multiple files per release?
6. Do clients need platform-specific assets like Windows/Linux/macOS builds?
7. Should tag-based access be fully active in v1, or should the schema/UI be prepared first and enforcement added in v1.1?
## 14. Recommended simplifications for v1
To keep the first version small and reliable:
- one artifact file per release;
- one main admin user;
- SQLite only;
- local filesystem storage only;
- one scope strategy per API key;
- project white-list and black-list in v1;
- tags supported in schema and project UI from day one;
- session auth for web UI, API key auth for clients;
- soft delete for projects/releases/API keys when possible.