# Changelog ## v2.9.0 ### Upgrading from v2.8 - TUF online keys are read only from `ONLINE_KEY_DIR///` (`` is the app owner, same pair as in `tuf_metadata///`). Move each key from `ONLINE_KEY_DIR/` there before or right after upgrading, otherwise publishing and re-signing fail and `timestamp.json` expires within a day: - find an app's online keyids: `jq -r '.signed.roles | (.targets, .snapshot, .timestamp).keyids[]' N.root.json` and `jq -r '.signed.delegations.roles[]?.keyids[]' N.targets.json`; - directories must be readable by UID `65532` and not be symlinks; - a key shared on purpose by apps of one admin goes into each app's directory; a keyid listed by two different admins is what this release blocks, do not copy it across. - If `ONLINE_KEY_DIR` is on a case-insensitive filesystem, check existing admin and app names for case-only duplicates, or move the directory to a case-sensitive filesystem. Renaming a TUF app does not move its metadata or keys. - `tuf-kms` prints the per-app directory from its next release. ### Security - TUF online keys are bound to one app, so an admin can no longer get metadata signed with another tenant's online keys by listing them in a bootstrapped root. - New usernames, and app names of one admin, that differ only by case are rejected; new admin usernames may not contain `-` or `/`, team usernames may not contain `/`. - Sessions are bound to the user's ID and a token version, and authorization uses the user type and ID instead of the username. - Team users are checked against their allowed apps, channels, platforms and archs on `/tuf/v1/artifacts/publish`, `/tuf/v1/artifacts/delete` and `/apps/update` before anything is read or written (403). - API tokens no longer read `/telemetry` or apps outside `allowed_apps` on `/search`. - `/app/create` and `/app/update` validate the app name. - `X-Device-ID` values over 128 bytes are not counted in telemetry. - Updated Go to 1.26.10, `go-jose` to 4.1.8 and `vite` to 6.4.5. ### Fixed - Team user access check errors return 500 instead of 403 or 401. - Granting a team user access to a resource they created no longer rewrites their other permissions. - `/artifact/delete` invalidates cached `/checkVersion` and `/apps/latest` responses and CDN copies; CDN copies are swept whenever `S3_BUCKET_NAME_CDN` is set. - TUF publish no longer treats a failed storage listing as a missing delegated role. - Telemetry counts a device that switches channel only in its last channel of the day. - The dashboard reloads itself instead of showing an error when a tab left open across a deploy requests old assets. ## v2.8.0 ### Changes - TUF timestamp and snapshot are re-signed in the background before they expire (checked every 5 minutes, re-signed within an hour of expiry), so metadata no longer expires between releases. - TUF metadata updates hold one per-app lock for the whole flow, so concurrent uploads, deletes and re-signs no longer race. ### Security - TUF root versions are anchored in Redis after verification: write access to the bucket alone can no longer replace the trusted root. Existing apps are verified from storage and anchored on first use. - Stricter verification of TUF metadata before it is re-signed. - TUF rejects RSA keys shorter than 2048 bits and ECDSA keys below P-256: online keys refuse to sign, and bootstrap, root rotation, delegations and offline signatures with such keys are refused. - Updated Go to 1.26.9 and `golang.org/x/net` to 0.60.0 to fix stdlib and `x/net` CVEs. - Generated passwords in the dashboard use crypto.getRandomValues instead of Math.random. - TUF rejects delegated role names containing path separators or "..", or named after a top-level role. ## v2.7.0 > **Note from the maintainer** > > faynoSync was meant from the start to be simple to work with. Over time it grew new features and became harder for end users to understand and set up — and since I work with infrastructure every day, I didn't see that problem myself. This release is about fixing it: fewer and simpler deployment guides, and a much easier way to get faynoSync running. > > The update logic itself barely changes, but the dashboard is now part of the API binary. Whether you are new to faynoSync or upgrading, please read the updated guides: > > - [Local Deployment](https://faynosync.com/docs/getting-started/local-deploy) > - [Production Deployment](https://faynosync.com/docs/getting-started/production-deploy) ### Upgrading from v2.6 - The API refuses to start if `JWT_SECRET` is shorter than 32 bytes. With `GIN_MODE=release` (the default in the image) it also refuses to start if `JWT_SECRET` or `API_KEY` starts with `insecure-dev-`. Check the length with `awk -F= '/^JWT_SECRET=/{print length($2)}' .env`. - The image is distroless and runs as UID `65532`: no shell, no `curl`. Mounted files (TUF online keys) must be readable by that UID; drop healthchecks that call `curl`/`sh` — the image has its own. - The dashboard is served by the API at `/dashboard/`. The `ku9nov/faynosync-dashboard` image is no longer published. - Run `faynoSync migrate up` after upgrading, as before. ### Changes - Dashboard embedded into the API (`DASHBOARD_ENABLED`, `DASHBOARD_TUF_METADATA_URL`); fonts and icons bundled, strict CSP. - The dashboard CSP allows images from any `http:`/`https:` origin, so app logos load from whichever storage or CDN serves them. - Distroless runtime image with a built-in `HEALTHCHECK` (`faynoSync healthcheck`); migrations embedded in the binary. - Release binaries for Linux and macOS (amd64, arm64) with `SHA256SUMS` and signed build provenance, for running without Docker. - Startup checks for `JWT_SECRET`; repository env files use `insecure-dev-*` placeholders. - `/health` checks Redis regardless of `PERFORMANCE_MODE`. - `.env.example` is the single env file for local runs, Docker Compose and CI; `.env.local` and `docker-compose/envs/backend.env` were removed. - `deploy/docker-compose.yaml`: the production compose file from [Production Deployment](https://faynosync.com/docs/getting-started/production-deploy), validated in CI. - The local stack is a single `docker-compose.yaml`; `docker-compose/services/*.yml` and `docker-compose.development.yaml` were removed. To run only the dependencies, use `docker compose up -d db cache s3 webui`. - Docker Compose runs MongoDB 8.0 (override with `MONGO_IMAGE`, e.g. `MONGO_IMAGE=mongo:7.0`) and Redis 8.10; CI runs the integration tests against MongoDB 7.0 and 8.0. An existing 7.0 data volume starts on 8.0 with `featureCompatibilityVersion` 7.0 until you raise it. ## v2.6.0 ### Breaking changes - `GET /apps/latest` returns installers only: update feeds, `.nupkg`, `.delta`, `.blockmap`, `.sig` and Tauri update bundles are excluded; `package=` for them returns `404`. - `GET /apps/latest` picks the newest published version per platform/arch (and per package with `package=`), not one version per channel. Versions with `rollout` below 100 are skipped. - `POST /upload`: an artifact that already exists, or a version in another channel, returns `409` (was `500`). - `POST /apps/update` rejects a file the version already has with `409` instead of overwriting it in storage while keeping the old hashes. Delete the artifact first to replace it. - Compound extensions are kept on upload: `.tar.gz`, `.tar.xz`, `.tar.bz2`, `.tar.zst`, `.app.tar.gz`, `.AppImage.tar.gz`, `.nsis.zip`, `.msi.zip` (were stored as `.gz`/`.zip`). For new uploads use `package=tar.gz` instead of `package=gz`. ### Features - `GET /apps/latest` entries include `version`. ### Fixes - `GET /apps/latest`: files with the same extension no longer overwrite each other (e.g. a delta `.nupkg` replacing the full one). - Tauri `/checkVersion` returns the update bundle deterministically; it could return the `.dmg` or `.sig` at random. - `POST /upload` and `POST /apps/update` check conflicts before writing: a rejected request no longer overwrites stored files or leaves a partially recorded version. - Conflict checks now cover uploads without channel, platform or arch (also presigned uploads). - electron-builder macOS builds with both `.zip.blockmap` and `.dmg.blockmap` are accepted. - A `.nupkg`, `.delta` or `.blockmap` sent again under a storage key the version already has returns `409`; it used to overwrite the stored file and record a second artifact with the same link. - `POST /apps/update` without `channel` stores files in the version's channel folder; they used to land in a path without the channel, apart from the rest of the version. - Channel, platform and arch names are looked up among the caller's own. ## v2.5.0 ### Breaking changes - Redis is always required: the API connects on startup and exits if Redis is unreachable. ### Performance - Storage clients are created once per process and reused. Credentials and `STORAGE_DRIVER` are read once — rotating a key needs a restart. - GCS: uniform bucket-level access is cached per bucket, removing a `bucket.Attrs` call from every upload and copy. ### Improvements - Log levels: operator-relevant events (logins, team users, TUF, report groups, feed materialization, deletions) at `info`; TUF metadata load failures and Redis read errors at `warn`; `/checkVersion` `checkResult` at `debug`. ### Security - `POST /login` and `POST /signup` are rate limited in Redis. Login: 10/min per IP, 5/min per username. Signup: 5/h per IP, 20/h total. Returns `429` with `Retry-After`; `503` if Redis fails. - New `TRUSTED_PROXIES` (IPs/CIDRs) controls which proxies may set the client IP via `X-Forwarded-For` / `X-Real-IP`. Unset trusts all, which allows IP spoofing — set it to your proxy/LB or `127.0.0.1`. - `/signup` is disabled when `API_KEY` is empty (previously anyone could create an admin). The key is compared in constant time. - `POST /login` no longer leaks username existence via response time. - `/download` debug log no longer includes presigned URL signatures. ### Fixes - Edge (CDN) responses for platforms without a `manual` updater are stored under `manual`, so SDK checks hit the edge. - `/checkVersion` no longer records telemetry when `ENABLE_TELEMETRY=false`. ## v2.4.0 ### Breaking changes - Team users' allowed lists for apps, channels, platforms and architectures are now enforced on `POST /upload` (they were ignored since v1.4.1). An upload outside them returns `403` before anything is written to storage. ### Features - Presigned uploads: `POST /upload/init` returns presigned PUT URLs, files go straight to storage, `POST /upload/complete` creates the version. No reverse proxy in the data path. Not supported on MinIO (`501`). - `PRESIGNED_UPLOAD_URL_TTL` sets the presigned URL lifetime (default `30m`, max `168h`). ### Fixes - TUF `timestamp.json` is written with `Cache-Control: public, max-age=60, must-revalidate`, so clients see a new publish within a minute instead of after the storage default cache time. ## v2.3.0 ### Breaking changes - Private access is now decided per app, not per instance. Private apps get `download_mode`: `unlisted` (anyone with the key — the old `ENABLE_PRIVATE_APP_DOWNLOADING=true` behaviour) or `strict` (download token, or JWT of the owning admin / a team user with `download` permission and the app allowed). Set on `POST /app/create`, changed on `POST /app/update`. - `ENABLE_PRIVATE_APP_DOWNLOADING` is deprecated — it now only sets the default `download_mode` for new private apps. - `GET /download` left the auth middleware. A denied request returns the same `404` as an unknown key. A JWT with access gets `{"download_url": ...}`; every other allowed request gets a `302` to the presigned URL. - `fns_` API tokens are not accepted on `/download`. - `GET /checkVersion` and `GET /apps/latest` now gate `strict` apps: without an `X-Download-Token` for the app and channel (or a JWT with access) they answer exactly as for an unknown app. They used to expose version, changelog, `critical` and private-bucket keys to anyone. `unlisted` apps are unchanged. - A private app can no longer enable `cdn_edge`. ### Features - Download tokens (`fnd_…`), scoped to an app and channel, sent in the `X-Download-Token` header. `POST /download-tokens/regenerate` creates or rotates one and returns the value once (only the hash is stored); `GET /download-tokens/list` lists them without values. `channel_id` may be omitted only while the app has no channel, otherwise `400`. ### Fixes - A private app with a feed-based updater (`velopack`, `sparkle`, `electron-builder`, `squirrel_windows`) is now rejected with `400` on upload and update. The combination never worked — clients cannot fetch a private app's feed. Use `manual`, `tauri` or `squirrel_darwin`, or a public app. - Velopack feeds and Sparkle appcasts are no longer materialized for private apps; they were written to the public bucket and exposed the version list and private-bucket keys. Feeds left by earlier versions are not removed automatically. - Responses of a private app are no longer cached under `PERFORMANCE_MODE` — the cache is read before any credential is known. Public apps are unaffected. - `POST /upload` returns `404` instead of `500` when the app does not exist. - The `404` of `POST /apps/update` now carries the same `app_name not found in apps_meta collection` message as every other unknown-app answer; the two `ErrAppNotFound` values were merged into one. - `POST /report-keys/regenerate` and `POST /download-tokens/regenerate` no longer answer `500`: `404` when the app or channel does not exist or belongs to another owner, `403` when a team user is refused by their permissions. - Deleting an app or a channel also deletes its download tokens; deleting an app also deletes its report key. ### Security - Upgraded `go.opentelemetry.io/otel/sdk` to v1.46.0 ## v2.2.0 ### Features - `DELETE /apps/delete` now accepts several version ids in one request (`?id=&id=`). One application per request, and nothing is deleted unless every id is valid. The limit is set by `MAX_BULK_DELETE_VERSIONS` (default: `10`). ### Fixes - Deleting a version now invalidates the response and CDN caches, so a deleted version is no longer served as the latest one. - A storage failure during deletion no longer aborts the remaining cleanup; the links left in the bucket are returned as `orphaned_links`. - Replaced `Fatal` logs in the delete and read paths, where a single database error shut the server down. - Deletion now resolves the app owner behind a team user, so a team user with the `delete` permission can remove versions and artifacts of the apps allowed to them instead of hitting an empty result. Access to the app is verified against the team user's allowed apps. - `CheckPrivate` now looks up the app by `app_name` **and** owner. ### Security - Upgraded `google.golang.org/grpc` to v1.83.2 ## v2.1.1 ### Security - Upgraded `google.golang.org/grpc` to v1.83.1 ## v2.1.0 ### Fixes - DigitalOcean Spaces: the private bucket client now signs against `S3_ENDPOINT_PRIVATE`, so a private bucket in another region is reached at its own regional endpoint instead of the public one. AWS S3 is unaffected — it keeps deriving the endpoint from the region. - DigitalOcean Spaces: public object URLs are now percent-encoded like the AWS driver already did, so keys containing spaces or special characters no longer produce broken links. ### Maintenance - Added new 'S3_REGION_PRIVATE' env. ## v2.0.0 ### Important Notes - **Milestone release**, not a rewrite. `v1.0.0` was an upload API with channels and a latest-version check; `v2.0.0` is a full update backend. See "The road from 1.0 to 2.0" below. - **No breaking changes** — a drop-in upgrade from `v1.6.5`. Deploy the new binary and run `./faynoSync migrate up`. ### Features (Sparkle) - Added a `sparkle` updater type for macOS Sparkle apps: upload a `generate_appcast` appcast plus its archives and `.delta` files. Each `` is stored verbatim and re-emitted on materialization, so elements faynoSync does not model round-trip faithfully — `sparkle:edSignature` and the whole `` subtree pass through untouched, while every enclosure URL is rewritten to its faynoSync link. Only publish, critical, changelog and `` are overlaid. - Feeds are materialized to `sparkle/{owner}/{app}/{platform}/{arch}/appcast.{channel}.xml` and regenerated on upload, edit and deletion. `POST /apps/update` ingests sparkle metadata too, and an appcast with no `` for a supplied archive is rejected. ### Performance - Feed materialization (sparkle and velopack) now rebuilds only the affected `(channel, platform, arch)` feeds instead of every feed of the app, turning a synchronous publish from `O(app feeds × versions)` into `O(touched feeds × versions)` — tens of milliseconds instead of ~0.5–1.5 s on a 20k–50k-version app. Falls back to a full regen when the touched tuples cannot be determined, so no feed is served stale. ### Security - Upgraded `google.golang.org/grpc` to v1.82.1 ### The road from 1.0 to 2.0 Per-release detail is in the `v1.x` sections below. - **Updaters** — native feeds for `electron-builder`, `squirrel_windows`, `squirrel_darwin`, `tauri`, `velopack` and `sparkle`, plus `default`. Binary and browser-extension app types. - **TUF** — signed metadata on `go-tuf/v2`: bootstrap, per-app isolation, the full root/targets/snapshot/timestamp and delegated lifecycle, offline signing, key rotation, per-role thresholds and expirations, Ed25519/ECDSA/RSA-PSS keys. Hardened in `v1.5.16`–`v1.5.17`; the `v1.5.0` pilot caveat no longer applies. - **Telemetry and reports** — per-app usage aggregation, crash/event ingest with signature-hash grouping, presigned detail blobs, rate limiting, and a triage lifecycle (`open`/`resolved`/`muted`, tags, notes, regression auto-reopen). - **Rollouts** — staged rollouts, intermediate required builds, and `possible_rollback` for clients ahead of the published version. - **Storage** — one `StorageClient` interface over AWS S3, MinIO/Garage, DigitalOcean Spaces and GCS; public/private/CDN bucket split with presigned URLs; Redis caching of `checkVersion` and `RELEASES` responses. - **Access control** — JWT auth, a team authorization matrix, scoped API tokens, `whoami`. - **Operations** — semver with build numbers, changelogs and critical flags, Slack notifications, explicit `migrate up`/`down`, and a grown integration plus TUF unit test suite. ## v1.6.5 ### Features - Added a lifecycle status to report groups (`open`/`resolved`/`muted`, defaulting to `open`). New groups are created `open`; the list API accepts a `status` filter (`status=open` also matches pre-existing groups that have no status field, `status=all` disables the filter). - Added `PATCH /reports/groups/:groupHash` (permission `edit`) as a partial-update endpoint for a group's `status`, `tags`, and `note` (any combination; at least one required). Resolving records `resolvedAt`/`resolvedBy`, any other status clears them. - Added free-form triage metadata independent of status: `tags` (up to 20, each `^[a-zA-Z0-9._-]{1,64}$`) and a `note` (up to 2000 chars). An empty tags array or empty note clears the field; both survive an auto-reopen. - Added regression auto-reopen: a new event on a `resolved` group flips it back to `open` and clears the resolution markers (tags/note are preserved), while `muted` groups stay suppressed. - Added `DELETE /reports/groups/:groupHash` (permission `delete`) to hard-delete a group together with its blob metadata and the underlying storage objects (best-effort, falling back to the storage lifecycle/TTL). All three endpoints are scoped to the requester's accessible apps. ## v1.6.4 ### Features (Velopack) - Added a `velopack` updater type. Upload accepts the `vpk`-generated `*-full.nupkg`/`*-delta.nupkg` packages plus `releases.{channel}.json`; the feed's `SHA1`/`SHA256`/`Size` are ingested verbatim into a separate `velopack` block on each artifact (kept apart from the TUF `hashes`/`length`), packages are stored under `velopack/{app}-{owner}/{version}/{channel}/{platform}/{arch}/` with their original filenames preserved. - Added Velopack feed generation from MongoDB (`velopack.BuildFeed`): PascalCase `Assets` with absolute `FileName` URLs, published-only, all Fulls plus deltas whose immediate lower base is published (deltas across an unpublish/delete hole are dropped, Full remains as fallback). - Added Mode 1 (CDN) write-through materialization to `S3_BUCKET_NAME` at `velopack/{owner}/{app}/{platform}/{arch}/releases.{channel}.json`, regenerated on upload, publish/unpublish, changelog/edit, and version/artifact deletion (velopack apps only). Empty feeds (`{"Assets":[]}`) are written to flush stale versions. - Added Mode 2 (dynamic API) route `GET /velopack/:owner/:app/:platform/:arch/*feed` that builds the feed per request and caps it at the required intermediate version (via the existing migration-step lookup) when `localVersion` is supplied. - Separated velopack installers from packages: installer files (anything that is not a `.nupkg` or `releases.*.json`) are now stored under a versioned `installers/{stem}-{version}-{platform}-{arch}.{ext}` key so the persisted artifact link is stable per version, while the flat, fixed-name installer (`velopack/{owner}/{app}/{platform}/{arch}/{file}`) is materialized on upload/update to keep pointing at the newest build. Added `CopyObject` to the storage interface (S3/base, MinIO, GCS) to back this copy-through. ### Features (Global) - Added staged rollouts. ## v1.6.3 ### Features - Added Redis caching for `squirrel_windows` updater responses, including the rewritten `RELEASES` feed served as a raw `text/plain` body (filename column rewritten to absolute storage URLs so Squirrel downloads each `.nupkg` directly from storage). - Added the `updater` parameter to the edge CDN object key so edge responses are cached per updater. ### Fixes - Team users now store artifacts under their admin: `UpdateSpecificApp` resolves the request owner via `ResolveRequestOwner` before uploading to S3. - `.blockmap` and `.nupkg` artifacts are now excluded from the `checkLatest` response since they are derived by updaters (electron-builder from the `.yml`, Squirrel from the `RELEASES` feed) and must not be returned. - Cached `squirrel_windows` `RELEASES` feeds no longer skew update analytics: the update status is now persisted in the cache entry (`HasUpdate`) and reused on cache hits, so outdated clients are correctly counted as outdated instead of falling through to `clients_using_latest_version` (the raw string body could not be inspected for `update_available`). ### Security - Updated `go.mongodb.org/mongo-driver` to 1.17.7. - Bumped `github.com/ku9nov/faynosync-sdk-go` to 0.2.0. ## v1.6.2 ### Security - Updated Go to 1.26.3 (`go.mod`, `Dockerfile`, CI workflow) to fix stdlib CVE-2026-* issues. - Updated `golang.org/x/crypto` to 0.52.0 to fix CVE-2026-* issues. - Updated `golang.org/x/net` to 0.55.0 to fix CVE-2026-* issues. ## v1.6.1 ### Fixes - Fixed version parsing on the latest-version check: `-` separators in the `version` query parameter are now normalized to `.` so values like `1-2-3` are parsed correctly. ### Dependencies - Bumped `tsx` to `^4.22.4` in the `tuf-client-ts` example, pulling in `esbuild` >=0.28.1 and resolving two Dependabot alerts (dev-only, example code). ## v1.6.0 ### Features - Added a crash/event reporting API: clients can ingest reports, which are deduplicated into groups by signature hash, with detail blobs stored in the private S3 bucket and served via presigned URLs. - Added report endpoints for ingest, group listing, group details, and presigned blob access. - Added per-key, per-group, and per-device+group rate limiting on report ingest. - Added report partitioning by app and supporting MongoDB indexes via new migrations. - Added `REPORTS_*` configuration (enable flag, body/blob size limits, blob retention, max blobs per group, storage prefix, rate limit). ## v1.5.17 ### Security (TUF) - Fixed delegated key rotation failing at the delegated-signing step; the rollback floor now derives from the trusted snapshot (`root → snapshot`) instead of re-verifying the orphaned old delegated blob through the just-rotated targets delegator. - Fixed trusted metadata read back from S3 without re-verifying its signature; all loaders now verify signature + expiration against the root chain before any authorization/version decision. - Fixed timestamp version rollback on a transient S3 download error; version is only reset to 1 when the object provably does not exist. - Fixed inconsistent locking across the snapshot/timestamp update paths; all three now run inside a single token-checked per-(admin,app) lock. - Fixed `AddArtifacts`/`RemoveArtifacts` mutating root/targets/delegated metadata loaded from S3 without verifying signatures first. - Fixed target hashes accepted without format validation; a well-formed 32-byte `sha256` is now required (invalid-hex raw-bytes fallback removed, non-positive lengths rejected). - Fixed unbounded expiration values from config/bootstrap; per-role bounds are now enforced (`timestamp ≤ snapshot ≤ targets ≤ root`). ## v1.5.16 ### Security (TUF) - Fixed snapshot lock key inconsistency that allowed concurrent artifact publish and force-online update to race and silently discard each other's snapshot changes. - Fixed silent verification skip in `PostMetadataSign` targets path: S3/unmarshal failures now surface as errors instead of being swallowed, preventing unverified metadata from advancing. - Fixed trusted metadata loaded from S3 without expiration check; expired metadata is now rejected before use. - Fixed root rotation staging that used fragile error string matching to detect wrong-version vs bad-signature failures. - Fixed `PostMetadataSign` Redis writes silently ignored on staging paths; errors are now propagated. - Fixed `loadTrustedRootMetadataFromS3` using raw `json.Unmarshal` instead of the library's `FromFile`; root is now loaded through the verified library path. ### Fixes (TUF) - Fixed snapshot `MetaFile` entries omitting hashes; snapshot now includes `sha256` hash and length for all referenced metadata files. - Fixed missing root signature verification in the `thresholdReached` targets signing path. - Fixed `GetConfig` returning only the first custom role expiration due to a loop variable bug. - Fixed `RemoveArtifacts` loading root by hardcoded version 1 instead of the latest version. - Fixed duplicate signing logic in `removeArtifactsFromDelegatedRole`. - Fixed MongoDB artifact status updated before TUF operation succeeds; status is now written only after a successful TUF commit. - Fixed temporary directories created in `cwd` instead of system temp. - Fixed `HelperGetPathForTarget` panicking on `os.Getwd()` failure; returns error instead. - Fixed task records stored in Redis with no TTL. - Fixed `CalculateExpirationDays` silently returning 365 on parse failure; returns error instead. - Removed unused `ctx context.Context` parameter from delegation helpers. ### Improvements (TUF) - Added `appName` validation to reject characters that could cause Redis key collisions or S3 path injection. - Replaced `KEYS` with `SCAN` in `GetMetadataSign` to avoid blocking Redis on large keyspaces. - Fixed `GetConfig` loop and `PutConfig` to respect online/offline mode for targets expiration. - Removed dead code: `GetBootstrapLocks`, `bootstrap()`, `bootstrapFinalize()`. ## v1.5.15 ### Features - Added `cdn` parameter to `POST /app/create` and `POST /app/update` endpoints, plus `S3_BUCKET_NAME_CDN` for CDN-targeted uploads. - Added batch object deletion support in storage clients to clean up related artifacts more efficiently. - Updated test setup to use SDK-backed integration flow. - Added telemetry beacon endpoint `/telemetry/beacon` to collect beacon requests and build allow-list index. ### Fixes - Fixed telemetry does not work when performance mode is enabled. ## v1.5.14 ### Features - Added `reports` parameter to `POST /app/create` and `POST /app/update` endpoints. - When `reports` is enabled, a public report key is created in the `report_keys` collection and bound to the app (one key per app). - Toggling `reports` on app update creates or removes the report key binding when the flag changes. - Added `GET /report-keys/list` to list report keys with app identity for the current owner (team users see only allowed apps). - Added `POST /report-keys/regenerate` to replace the report key value for a given app. ### Database - Added `report_keys` collection indexes: unique `app_id` and owner lookup for listing. ### Maintenance - Added integration tests: `TestListReportKeys`, `TestListReportKeysNoValues`, `TestListReportKeysWithSecondaryUser`, `TestListReportKeysAdminUserBeforeTeamUser`, `TestListReportKeysTeamUserPermissionDenied`, `TestListReportKeysTeamUser`, `TestRegenerateReportKey`, `TestFailedRegenerateReportKeyWithSecondaryUser`, `TestFailedRegenerateReportKeyWithTeamUser`, `TestRegenerateReportKeyTeamUser`, `TestListReportKeysNoValuesAfterUpdateAppReportsToFalse`. ## v1.5.13 ### Features - Added `POST /tuf/v1/metadata/delegated/rotate` to rotate delegated targets metadata. ### Maintenance - Upgraded `github.com/slack-go/slack` from v0.14.0 to v0.23.1. - Refactored the TUF metadata package by moving shared helpers and rotation orchestration from `metadata.go` into `metadata_utils.go`. - Added `metadata_delegated_rotate_test.go` covering delegated rotation staging and related error paths. ### API Tooling - Updated the Postman collection with requests for delegated metadata rotation. ## v1.5.12 ### Improvements - Decoupled MongoDB migrations from API startup: the server now starts with `./faynoSync` only, and migrations run explicitly via `./faynoSync migrate up` or `./faynoSync migrate down`. ### Features - Added `GET /tuf/v1/metadata/targets` and `GET /tuf/v1/metadata/delegated` endpoints to retrieve TUF metadata for targets and delegated roles. - Added tuf typescript example. ### Fixes - Fixed telemetry period aggregation for `range=week` and `range=month` to deduplicate repeated `client_id` values across days instead of summing daily set sizes. - Updated telemetry integration coverage to validate that `unique_clients`, `clients_using_latest_version`, and `clients_outdated` remain deduplicated at period level. ## v1.5.11 ### Features - Added `POST /tuf/v1/bootstrap/recovery` to rebuild bootstrap Redis settings from persisted TUF metadata for already initialized repositories. - Added asynchronous `bootstrap_recovery` task flow with lock protection, recovery prechecks, timeout support, and task status reporting. ### Security & Access Control - Added RBAC edit permission checks for TUF task status, artifact publish, and artifact delete endpoints. - Added owner resolution middleware for team users so TUF artifact operations run under resolved owner context. ### Reliability - Unified bootstrap settings persistence and recovery via a shared Redis save path, including delegated role expirations and `ROOT_SIGNING` initialization. ### API Tooling - Updated Postman collection with bootstrap recovery API request examples. ## v1.5.10 ### Dependencies - Upgraded `go.opentelemetry.io/otel`, `go.opentelemetry.io/otel/metric`, `go.opentelemetry.io/otel/sdk`, `go.opentelemetry.io/otel/sdk/metric`, and `go.opentelemetry.io/otel/trace` to `v1.43.0`. ### Security & Signing Improvements - Extended TUF online signing to support multiple key types (Ed25519, ECDSA, and RSA-PSS) loaded from filesystem private keys. - Added signer/verifier construction by key type with explicit keyid-to-key-material validation to prevent mismatched key usage. ### Maintenance - Removed legacy bootstrap generation API surface (`/tuf/v1/bootstrap/generate` and `/tuf/v1/bootstrap/locks`) and deleted obsolete generate handlers/tests. ## v1.5.9 ### Dependencies - Upgraded `github.com/aws/aws-sdk-go-v2/service/s3` to `v1.97.3`. - Upgraded `github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream` to `v1.7.8`. ## v1.5.8 ### Dependencies - Upgraded `github.com/go-jose/go-jose/v4` to `v4.1.4`. ## v1.5.7 ### Dependencies - Upgraded Go to v1.26.1. - Upgraded `google.golang.org/grpc` to v1.79.3. ## v1.5.6 ### Maintenance - Migrated from MinIO to Garage for default local S3 storage. ## v1.5.5 ### Features - Slack notifications now reuse a single mutable message per app version across artifact create, update, and delete flows, with Redis-backed state, configurable TTL, and cleanup when a version is deleted. ## v1.5.4 ### Dependencies - Upgraded `go.opentelemetry.io/otel/sdk` to v1.40.0. ### Improvements - Token create endpoint: changed response status code for token creation. ## v1.5.3 ### Features - **API Tokens**: Added API tokens for secure and scoped access to the API. ### Testing - **API Tokens integration tests**: Added integration tests for creating, listing, and deleting API tokens. ## v1.5.2 * **New Features** * Added multi-signer support for secure cryptographic operations across roles * Introduced per-role key threshold configuration for enhanced key management * Enhanced bootstrap process with conflict detection and persistent metadata validation * **Improvements** * More detailed error messages including per-key failure information * Better root metadata versioning with intelligent fallback logic * Added context-aware cancellation support and improved compatibility with large datasets ## v1.5.1 ### Bug Fixes - **Fixed incorrect key usage in metadata signing**: Each TUF role (snapshot, targets, delegated roles) now uses its own dedicated key for signing instead of incorrectly using the timestamp key. This ensures proper TUF specification compliance. ### Features - **Online metadata update endpoint**: Added `POST /tuf/v1/metadata/online` endpoint for force updating online metadata roles (snapshot, timestamp, targets, delegated roles) without requiring offline signing workflow. - **Metadata sign deletion endpoint**: Added `POST /tuf/v1/metadata/sign/delete` endpoint for deleting pending metadata signatures from Redis. ### Improvements - **Delegated roles expiration**: Each delegated role now uses its own expiration configuration from Redis instead of a shared BINS_EXPIRATION setting. ### Testing - **TUF unit test coverage**: Added unit tests for TUF metadata, metadata root, config, storage, signing, tasks, settings, delegations, artifacts, generate, bootstrap, delete, converter and utils. Storage mock is now global for tests. ### Security Fixes - Fix: go-tuf affected by client DoS via malformed server response - Fix: go-tuf improperly validates the configured threshold for delegations - Fix: sigstore legacy TUF client allows for arbitrary file writes with target cache path traversal - Fix: go-tuf Path Traversal in TAP 4 Multirepo Client Allows Arbitrary File Write via Malicious Repository Names - Upgraded github.com/theupdateframework/go-tuf/v2 to version v2.4.1 ## v1.5.0 ### Important Notes - **⚠️ Pilot Version**: This release introduces a pilot/experimental TUF (The Update Framework) implementation. While the functionality is operational, it requires significant additional work and enhancements before production deployment. This version should be used for testing, evaluation, and gathering feedback. ### Features - **TUF Bootstrap System**: Complete bootstrap functionality for initializing TUF repositories - Repository initialization with secure root key generation - Redis-based locking mechanism to prevent concurrent bootstrap operations - Real-time bootstrap status and lock monitoring - API endpoints: `GET /tuf/v1/bootstrap`, `POST /tuf/v1/bootstrap` - **TUF Artifacts Management**: Publish and delete application artifacts in TUF repository - Automatic metadata generation for published artifacts - Automatic hash calculation (SHA256, SHA512) for artifact integrity verification - Separate TUF metadata per application - Conversion between application artifacts and TUF targets format - API endpoints: `POST /tuf/v1/artifacts/publish`, `POST /tuf/v1/artifacts/delete` - **TUF Metadata Management**: Comprehensive metadata operations - Metadata rotation for root, targets, snapshot, and timestamp roles - Root keys rotation with offline signing support - Offline metadata signing capabilities - Trusted root metadata retrieval - Support for role-based access control with delegations - Configurable expiration and threshold per role - API endpoints: `POST /tuf/v1/metadata`, `GET /tuf/v1/metadata/sign`, `POST /tuf/v1/metadata/sign`, `GET /tuf/v1/metadata/root` - **TUF Configuration Management**: Get and update TUF repository settings - Configurable expiration times, thresholds, and key counts for each metadata role - Redis-based configuration storage with per-admin, per-app isolation - API endpoints: `GET /tuf/v1/config`, `PUT /tuf/v1/config` - **Task System**: Asynchronous task-based system for long-running operations - Real-time task status monitoring via Redis - Task states: PENDING, IN_PROGRESS, SUCCESS, ERRORED - API endpoint: `GET /tuf/v1/task` - **Storage Enhancements**: Extended storage client capabilities - Added `DownloadObject` method to all storage clients (S3, GCS, MinIO) - Added `ListObjects` method for listing objects with prefix filtering - Unified storage interface across all providers - **Database Integration**: Enhanced artifact model and TUF state tracking - Added `tuf_signed` field to artifacts - Added hash fields (SHA256, SHA512) to artifacts - Added `length` field to artifacts - TUF publish state tracking in MongoDB ### Maintenance - Updated Go dependencies with TUF-related libraries (`go-tuf/v2`) - Modular TUF architecture organized into separate packages (bootstrap, artifacts, metadata, config, tasks, signing, storage, delegations) - All TUF endpoints require authentication and admin-only middleware - Enhanced error handling and logging for TUF operations ## v1.4.6 ### Security Fixes - Fix: golang.org/x/crypto/ssh allows an attacker to cause unbounded memory consumption - Fix: golang.org/x/crypto/ssh/agent vulnerable to panic if message is malformed due to out of bounds read - Upgraded golang.org/x/crypto to version 0.45.0 ## v1.4.5 ### Features - Changed `checkLatest` response: when the client app is newer than the published version, now returns a `possible_rollback` field with a URL to roll back. - Add `tauri` updater. ## v1.4.4 ### Features - Change request types for channels, platforms, and architectures from form-data to application/json - Add `electron-builder` and `squirrel_darwin` updaters. - Add `squirrel_windows` updater. ## v1.4.3 ### Features - **Storage Polymorphism**: Implemented polymorphic storage architecture for S3-compatible services - Added `StorageClient` interface for unified storage operations - Created factory pattern for dynamic storage client creation - Introduced base S3 client for common S3-compatible functionality - **Cloud Provider Support**: Added support for multiple cloud storage providers - **DigitalOcean Spaces**: Full S3-compatible storage support with presigned URLs - **Google Cloud Storage**: Native GCS integration with service account authentication - **Enhanced URL Parsing**: Improved URL parsing for different storage providers - Support for DigitalOcean Spaces URL format: `bucket-name.region.digitaloceanspaces.com/object-key` - Support for Google Cloud Storage URL format: `storage.googleapis.com/bucket-name/object-key` - Enhanced AWS S3 URL parsing for both virtual-hosted and legacy formats - Maintained MinIO URL parsing compatibility ### Configuration - **Environment Variable Renaming**: Improved environment variable naming for better clarity and organization - Renamed `S3_BUCKET_NAME` to `S3_BUCKET_NAME_PRIVATE` for private bucket configuration - Renamed `S3_BUCKET_NAME_PUBLIC` to `S3_BUCKET_NAME` for public bucket configuration - Renamed `S3_ENDPOINT` to `S3_ENDPOINT_PRIVATE` for private bucket endpoint - Renamed `S3_ENDPOINT_PUBLIC` to `S3_ENDPOINT` for public bucket endpoint - Updated all configuration files and documentation to reflect the new naming convention - Added new environment variables for cloud storage configuration: - `STORAGE_DRIVER`: Support extended to include `digitalocean`, and `gcp`. - `GCS_CREDENTIALS_FILE`: Path to Google Cloud service account credentials - `GCS_SERVICE_ACCOUNT_EMAIL`: GCS service account email for presigned URLs - `GCS_PRIVATE_KEY`: GCS private key for presigned URL generation ### Maintenance - Updated Go dependencies to latest versions - Enhanced error handling and logging across storage operations - Improved code organization with dedicated storage package structure ## v1.4.2 ### Bug Fixes - Fixed an issue where the build name on S3 could be generated incorrectly. ### Features - Updated the search app route; added sorting by channels, platforms, architectures, and published and critical status. - Added intermediate required builds. More details here: https://ku9nov.github.io/faynoSync-site/docs/intermediate_build - Added telemetry. More details here: https://ku9nov.github.io/faynoSync-site/docs/telemetry ### Maintenance - New functions are covered by tests. ## v1.4.1 ### Important Notes - Team users update route using the id instead of the username. - Optimize check permisiion logic. - Add update admin route. ### Potential bug fix - Fix: golang.org/x/net vulnerable to Cross-site Scripting ## v1.4.0 ### Features - Enhanced the `DeleteDocument` function to check for related documents in the `apps` collection before deletion. - Add `download` route. - Add support for private S3 bucket. - Add presigned URL support for MinIO and AWS. - Add team based authorization matrix - Add `whoami` route. ## v1.3.12 ### Features - Enhanced API Responses: Updated the return results for "Get All Apps" and "Search by Name" requests. - Pagination Improvement: Introduced a limit for "Get Apps" requests to optimize performance. - Add application logo support. - Add application description. - Add artifact deletion route. ## v1.3.11 ### Potential bug fix - Fix: jwt-go allows excessive memory allocation during header parsing - Fix: HTTP Proxy bypass using IPv6 Zone IDs in golang.org/x/net ## v1.3.10 ### Potential bug fix - Fix: Non-linear parsing of case-insensitive content in golang.org/x/net/html - Fix: Misuse of ServerConfig.PublicKeyCallback may cause authorization bypass in golang.org/x/crypto ## v1.3.9 ### Bug Fixes - Fixed issue where MongoDB documents were sorted incorrectly by version string for the /apps/latest route. - Fix MinIO URLs for correct working on localhost. - Resolve the issue where the /checkVersion route returns an incorrect link because all artifacts have the same extension. ### Maintenance - Added new mongoDB indexes for `apps` collection. ## v1.3.8 ### Potential bug fix - Fix: Bad documentation of error handling in `ParseWithClaims` can lead to potentially dangerous situations ## v1.3.7 ### Features - Added `binary` app support. - Allowed `-` in platform name. - Added `Slack` notifications. - Added required parameter validation for `app_name` and `channel` in `FetchLatestVersionOfApp` endpoint. Returns an error if either parameter is missing. - Added a database connection check to the health check. ### Bug Fixes - Added validateParams to Update\Create app/channel/platform/arch - Fixed an issue with `CheckLatestVersion` where cached metadata values persisted across calls, by moving `appMeta`, `channelMeta`, `platformMeta`, and `archMeta` to local variables. ## v1.3.6 ### Important Notes - **Important**: Fix "Authorization bypass in github.com/dgrijalva/jwt-go" - **Important**: Now we can upload applications with the same extension, but this is only possible if the architectures are different. ### Bug Fixes Fix: allow spaces in app name validation ### Features - Added `browser extension` support. - Implemented Performance Mode to optimize the `checkVersion` API request by caching responses in Redis. - Added `link` encoding. - Added `build number` to version. (0.0.1 => 0.0.1.137) - Added new info API route which return links to latest build. ### Maintenance - Multiple API routes were renamed. - Added `semver` versions support. ## v1.3.5 ### Important Notes - **Important**: The authentication flow has changed to JWT. - **Important**: The route for deleting a specific app by ID has been renamed to `/apps/delete` from `/deleteApp`. - **Important**: `/deleteApp` is now a new function. - **Important**: The route for updating specific app by ID has been renamed to `/apps/update` from `/update`. - **Important**: `Archs, App names, Platform, channels` can be changed now. ### Features - Added `Create, List, Delete` App functionality. It now works like `Platforms, Channels, Archs`. - Added new tests. - Added `UpdateApp, UpdateChannel, UpdatePlatform, UpdateArch` routes. ### Maintenance - `Platforms`, `Channels`, `Archs` have been moved to the `apps_meta` collection. ## v1.3.4 ### Features - Added `Changelog` to check latest version response. - Added `Critical` parameter. ### Maintenance - Refactored `List`, `Create`, `Delete` mongodb functions. ## v1.3.3 ### Important Notes - **Important**: The `POST` request uses body form-data instead of URL parameters. Example: ``` --form 'data="{\"app_name\":\"app\",\"version\":\"0.0.2\",\"channel\":\"\",\"publish\":true,\"platform\":\"\",\"arch\":\"\"}"' ``` ### Maintenance - Updated Go modules. - Refactored `createChannel`, `createPlatform`, and `createArch` functions. - Refactored `deleteChannel`, `deletePlatform`, and `deleteArch` functions. - All tests were updated for the new functionality. ### Features - Added `Changelog` functionality to handle `upload` and `update` requests. - Updated logging flow in the application. - Refactored `ValidateParams` function for parsing the request body. ## v1.3.2 ### Important Notes - **Important**: Updated `Postman` template. ### Maintenance - Added configurations for `one command` local development. - Added `Dockerfile` - Added `Minio` deployment to `docker compose` ### Features - Add functionality to handle cases where the client app sends `platform` and `arch` parameters that do not exist. In such cases, the system should ignore the parameters and return the appropriate version. - If the client app sends either `platform` or `arch` parameters, and they exist in `faynoSync`, the system should return the closest matching version to the request. Any incorrect `platform` or `arch` names should result in appropriate error messages. - Implemented a new API - SignUp - Update (App) - Implemented new e2e test - TestSignUp - TestUpdate ## v1.3.1 ### Important Notes App is renamed to `faynoSync`. - **Important**: `channel_name` parameter renamed to `channel` - **Important**: Updated `Postman` template. - **Important**: `checkVersion` changed from `POST` to `GET` - **Important**: MongoDB architecture is changed to all in one collection. Changed mongoDB objects structure. ### Maintenance - Added new `mongodb` migrations. - Added `flutter` example app. ### Features - :tada: Implemented the published feature. Now the API returns only published versions. - Implement multiple files uploading. - Updated `Postman` collection. - Implemented a new APIs - Creating platforms - Removing platforms - List platforms - Creating architectures - Removing architectures - List architectures - Implemented new e2e tests - TestPlatformCreate - TestUploadAppWithoutPlatform - TestArchCreate - TestUploadAppWithoutArch - TestDeletePlatform - TestDeleteArch - TestListArchs - TestListPlatforms - TestListChannels - TestListArchsWhenExist - TestListPlatformsWhenExist - TestListChannelsWhenExist Other tests is adapted. ### Bug Fixes - Fixed a critical issue where returned versions were incorrectly compared when the version numbers were greater than 9 (e.g., 0.0.14). ## v1.2.0 ### Important Notes - **Important**: Changed the method of creating an admin user. Now, after creation, the web server starts and operates in normal mode, so it is recommended to use creation in complex with migration. ### Maintenance - Added `MONGODB_URL_TESTS` to `.env.example` ### Docker - Added `mongoDB` for testing to `docker compose` configuration. ### Features - :tada: Implemented e2e tests - TestHealthCheck - TestLogin - TestUploadApp - TestUploadDuplicateApp (expected result from api "failed") - TestDeleteApp - TestChannelCreateNightly - TestChannelCreateStable - TestUploadAppWithoutChannel (expected result from api "failed") - TestMultipleUploadWithChannels - TestSearchApp - TestCheckVersionLatestVersion - TestMultipleDelete - TestDeleteNightlyChannel - TestDeleteStableChannel ## v1.1.0 ### Important Notes - **Important**: After first creating channel, field `channel_name` is required. Added a set of functionalities for the operation and support of deployment channels. Many checks have been added for a more accurate and expected behavior of the application. ### Features - :tada: Implemented a new feature - Creating channels - Removing channels - List channels ## v1.0.0 ### Important Notes - **Important**: `Bearer` in the authorization header is now required. - **Important**: Updated API reference and README. ### Maintenance - Added initial functionality - `MongoDB` functionalitty - `MongoDB` migrations with indexing - `s3` support - `JavaScript` and `Python` usage examples - `Terraform` configuration for creating `s3` bucket - Added `Postman` collection example - Added `.env.example` ### Features - :tada: Implemented a new features - Authentication (Auth) - Uploading - Removing - Check latest version feature - Searching ### Docker - Added `docker-compose` file. ## v0.0.1 (Example) ### Important Notes - **Important**: Removed something - **Important**: Updated something - App now requires something ### Maintenance - Removed a redundant feature - Added a new functionality - Improved overall performance - Enhanced user interface responsiveness ### Features - :tada: Implemented a new feature - This feature allows users to... - Added a user profile customization option ### Bug Fixes - Fixed a critical issue that caused...