Skip to content

Releases

v0.3.0

Choose a tag to compare

@andprov andprov released this 28 Oct 19:44
cf83e8c

What's Changed

A security hardening release: nginx no longer runs as root, password reset and activation codes can't be used twice, the app sends stricter browser security headers, and the published images are checked for known vulnerabilities every week.

⚠️ Deployment: nginx listens on 8080/8443 inside the containers

nginx in the nginx and frontend images now runs as an unprivileged user and listens on container ports 8080 (HTTP) and 8443 (HTTPS) instead of 80 and 443. The ports on the host stay 80 and 443.

  • Standard update (download the new docker-compose.yml, run docker compose up -d): nothing else to do.
  • Custom setup: if you changed the nginx ports in your own compose file or docker-compose.override.yml, or put your own reverse proxy in front of the frontend container, point them to 8080/8443. The new images don't work with the old docker-compose.yml.
  • First start after the update: HTTPS may be unavailable for about a minute. The certbot container now gives nginx read access to the certificates in the letsencrypt volume; until it does, nginx serves HTTP only, then switches to HTTPS by itself.

Security

  • Codes can't be used twice. Two simultaneous requests with the same password reset or activation code could both succeed. Now only the first one does; the second gets 400 with an invalid code error. Wrong-code attempts are also counted atomically, so parallel requests can't go over the attempt limit.
  • Password reset checks the new password first. POST /api/auth/password/reset validates the new password before it looks up the email and the code. A weak password now gets its own error (e.g. password_too_short) whatever the email and code are. The status is still 400.
  • Content-Security-Policy and Permissions-Policy headers. Scripts, styles and fonts load only from the site itself, and the site can't be embedded in a frame. The API docs (/api/docs, /api/swagger, /api/redoc) get a looser policy for their CDN assets. Browser features the app doesn't use (camera, microphone, geolocation, payment and others) are turned off. If you changed the frontend to load scripts, styles or fonts from other domains, update the policy in gateway/nginx/default.conf.template.
  • Leaner images. OS packages are upgraded on every build. The backend image no longer contains pip, and its dependencies are installed with exact versions from uv.lock. FastAPI is installed without the standard extras, which pulled in tools the app doesn't use. The certbot image is pinned to a certbot release instead of latest.

CI

  • Released images are scanned with Trivy every week.
  • GitHub Actions are pinned by commit SHA; Dependabot keeps them and the certbot image up to date.

No database migrations.

Full Changelog: v0.2.0...v0.3.0

v0.2.0

Choose a tag to compare

@andprov andprov released this 27 Oct 20:49
d0a19b7

What's Changed

An account must now be activated before its settings can be changed or the account deleted. The web app already worked this way; the API now enforces it as well.

⚠️ Breaking: activation required for account changes

These endpoints now return 403 with code access_denied for an account that is not activated:

  • PATCH /api/users/me/password
  • PATCH /api/users/me/email
  • PATCH /api/users/me/locale
  • PATCH /api/users/me/time-format
  • DELETE /api/users/me

Before this change they worked for any signed-in user. Projects and timers already required activation.

A user who is not activated can still:

  • load their profile with GET /api/users/me
  • activate the account, request a new code and check the resend cooldown
  • list and revoke sessions, and sign out

API clients that let users change settings or delete the account before activation need to handle the new 403.

Internal

  • The activation check moved from the adapters layer into the application layer.
  • Use cases for creating a project, starting a timer and adding a manual time entry now take the user id as a separate parameter instead of inside the request DTO with the rest of the data.

No database migrations.

Full Changelog: v0.1.12...v0.2.0

v0.1.12

Choose a tag to compare

@andprov andprov released this 16 Oct 09:22
3ed27ee

What's Changed

The Projects and Timers pages now use the same compact card layout as the mobile app.

Timers

  • Cards instead of a table. Each time entry is a card: project color and name, start and end time, duration, and amount for billable entries. On narrow screens the list no longer scrolls sideways.
  • Entries grouped by day. A date header with the weekday, e.g. Tue, 22.09.2026, comes before each day's entries, as in the mobile app. The date is no longer repeated on every entry.
  • 10 entries per page instead of 15.
  • Selecting entries for bulk delete works as before: use the checkbox on a card, or Select all above the list. Running timers are shown in red and can't be selected or edited.

Projects

  • Smaller cards. Project cards take less vertical space.
  • 10 projects per page instead of 6.

Both pages

  • Pagination follows the list. The page buttons now come right after the last card instead of staying at a fixed height. On a page with fewer entries they move up.

No backend changes and no database migrations.

Full Changelog: v0.1.11...v0.1.12

v0.1.11

Choose a tag to compare

@andprov andprov released this 01 Oct 15:32
f6e6f5a

What's Changed

Fixed

  • Report CSV filename. The period dates in the exported filename now use the user's local time instead of UTC. Previously, in time zones east of UTC (e.g. Moscow) the start date was shifted one day back: a September report was saved as report_2026-08-31_2026-09-30.csv. West of UTC, the end date was shifted one day forward. The filename now matches the selected period: report_2026-09-01_2026-09-30.csv.

Report contents were not affected: reports always included exactly the entries for the selected period.

Full Changelog: v0.1.10...v0.1.11

v0.1.10

Choose a tag to compare

@andprov andprov released this 29 Sep 17:48
422b4b6

What's Changed

SEO

  • locale in seo.config.json. Sets og:locale in the language_TERRITORY form Open Graph expects, e.g. ru_RU or en_US. Before this change og:locale was taken from lang, so "lang": "ru" produced ru. Without locale, the old behavior is kept: lang with - replaced by _.
  • imageWidth and imageHeight in seo.config.json. Emitted as og:image:width and og:image:height.
  • Validation. locale must be a string, and imageWidth and imageHeight must be positive integers. An invalid value stops the frontend container, and docker compose logs frontend shows why.
  • seo.config.example.json and the SEO table in docs/DEPLOY.md describe the new fields.

Full Changelog: v0.1.9...v0.1.10

v0.1.9

Choose a tag to compare

@andprov andprov released this 28 Sep 21:02
65d9f04

What's Changed

TimeGrip now ships as ready-made Docker images. A server needs three files from the release and one command. The repository is no longer needed to deploy, and HTTPS certificates are issued without manual steps.

Deployment

  • Published images for amd64 and arm64. timegrip-backend, timegrip-frontend, timegrip-nginx and timegrip-certbot are published to GitHub Container Registry for linux/amd64 and linux/arm64, so TimeGrip also runs on a Raspberry Pi with a 64-bit OS.
  • Deploy with docker compose up -d. Each release carries docker-compose.yml, env.example and seo.config.example.json. Put your .env and seo.config.json next to the compose file and start the stack. The compose file runs the images of its release.
  • Automatic HTTPS. certbot requests the Let's Encrypt certificate on the first start and retries every 15 minutes if the request fails. nginx switches to HTTPS within a minute after the certificate appears and picks up renewed certificates by itself. Running docker compose run --rm certbot obtain and restarting nginx are no longer needed. After changing DOMAIN_ALIASES or turning off CERTBOT_STAGING, docker compose up -d reissues the certificate.
  • SEO is applied when the frontend starts. seo.config.json is mounted into the frontend container instead of being built into it, so one image serves any instance. The file now lives next to docker-compose.yml, and its format is unchanged. After editing it, run docker compose restart frontend.
  • Postgres is no longer published on the host, so the stack starts on servers that already run their own Postgres. To reach the database from the host, see Database in docs/DEPLOY.md.

Documentation

  • Quick start in the README now deploys from the release files. Building from source moved to Try it locally and is marked as for evaluation and development only.
  • docs/DEPLOY.md is rewritten for image-based deployment: server requirements, configuration, updates, certificates, database access and backups.

Upgrade notes

No application changes and no database migrations. To update an instance that runs from a repository clone:

  1. Move frontend/seo.config.json to seo.config.json in the repository root, or set SEO_CONFIG_FILE in .env. Without the file, docker compose up stops with an error. Set SEO_CONFIG_FILE=/dev/null to run without SEO tags.
  2. The Compose project name is now fixed to timegrip. If your checkout directory has a different name, add COMPOSE_PROJECT_NAME=<directory name> to .env, so the existing database and certificate volumes are reused.
  3. If anything on the host connected to Postgres on 127.0.0.1:5432, publish the port again in docker-compose.override.yml (see Database in docs/DEPLOY.md).
  4. Update and restart:
    git pull
    docker compose up -d

v0.1.8

Choose a tag to compare

@andprov andprov released this 28 Sep 18:30
e06099b

What's Changed

This release reduces how many requests the web client sends to the server and fixes two stale-data issues.

Improvements

  • Faster dashboard filters. Changing the project or billable filter on the dashboard now updates the charts right away. The server is queried again only when the date range changes.
  • Fewer requests when starting or stopping a timer. The client uses the server's response directly and no longer asks again for the running timer.
  • Fewer requests when editing timers. Adding, editing or deleting a timer reloads only the timer lists, because these actions can't affect the running timer.
  • No extra profile reload. The profile is not fetched again after you sign up, activate your account, or change your email or time format.
  • Revoking a session removes it from the list without reloading the list.
  • Faster loading of large datasets. When the dashboard or a report spans many pages of timers or projects, the pages now load in parallel instead of one after another.

Fixes

  • Signing out now clears cached data. Before this fix, another account signing in on the same browser tab could briefly see the previous account's projects and timers.
  • Timer lists now update after a project is deleted (its timers are deleted with it) or archived and restored.

Full Changelog: v0.1.7...v0.1.8

v0.1.7

Choose a tag to compare

@andprov andprov released this 28 Sep 17:37
daacab4

What's Changed

Documentation

  • New README. It now opens with what TimeGrip is, dashboard screenshots
    for the light and dark themes, a feature list and a short "Why TimeGrip"
    section, followed by a quick start with Docker and sample data.
  • Russian README. README.ru.md is added, with a language badge at the
    top of each README to switch between the two.
  • Guides moved to docs/. Deployment, SEO configuration and HTTPS setup
    are now in docs/DEPLOY.md. Running the backend and frontend without
    Docker, tests and linting are in docs/DEVELOPMENT.md.
  • Quick start covers local .env setup: set SECRET_KEY and
    EMAIL_SENDER_BACKEND=console, so sign-up activation codes are written to
    the outbox_email log instead of being sent.

Upgrade notes

Documentation only. The backend version is bumped to 0.1.7 with no
functional changes, no migrations and no new configuration. Running
instances don't need to be updated.

Full Changelog: v0.1.6...v0.1.7

v0.1.6

Choose a tag to compare

@andprov andprov released this 25 Sep 07:28
1921e20

What's Changed

Improvements

  • Web: fewer redundant API requests. Switching browser tabs or moving
    between pages no longer refetches timers and projects loaded within the
    last 30 seconds (projects: 5 minutes). Changes you make in the app still
    refresh the data immediately, and the running timer is still updated
    every minute, so timers started on another device show up as before.

Maintenance

  • Backend: SQLAlchemy is now pinned to sqlalchemy[asyncio]>=2.0,<2.1.

Full Changelog: v0.1.5...v0.1.6

v0.1.5

Choose a tag to compare

@andprov andprov released this 24 Sep 21:40
201ed67

What's Changed

Starting a timer offline

POST /timer/start now accepts an optional start_time field. A client that started a timer while offline can send the time it was actually started once it's back online. If the field is left out, the timer starts now, as before.

Full Changelog: v0.1.4...v0.1.5