Clean multi-tenant time tracking software with projects, clients, task boards, browser reminders, weekly timesheet approvals, polished CSV/PDF timesheet exports, full audit logging, captured server errors, and Postgres-backed workspace data.
- Time tracking. Start/stop timer, manual entries, billable flag, project rates, weekly/monthly/financial year/custom period filters, CSV and PDF export with configurable GST.
- Clients & projects. Clients as first-class entities (contact, address, currency, default rate). Projects reference a client so renames flow through to historical entries.
- Approvals. Weekly timesheet periods per user with
draft → submitted → approved/rejectedlifecycle. Entries inside an approved week are locked until an admin reopens the period. - Audit log. Every mutating action on tenant data is recorded
(
who, what, when, before, after) and visible to admins in-app and via/api/audit-log. - Error log. Server-side exceptions are captured to
error_logand surfaced to admins for incident triage. - Multi-tenant. Organizations with admin/member roles, invite flow with email delivery via Resend, two-factor authentication via better-auth.
- API. Documented JSON API under
/api/*(current v1 surface) — seedocs/api.md./api/v1/healthprovides a liveness probe.
docker compose up -d
cd src/client/next-landing-page
npm install
npm run auth:migrate # better-auth tables
npm run db:migrate # SkyTime schema
npm run devThe app boots at http://localhost:3000. Sign up creates a user; the first sign-in prompts for an organization name.
- Google Places address autocomplete. Set
NEXT_PUBLIC_GOOGLE_MAPS_API_KEYto a Google Maps JavaScript API key with the Places library enabled. When set, the client address field offers type-ahead address suggestions. When unset, the field behaves as a plain text input. - Email delivery. Configure
RESEND_API_KEYandRESEND_FROMfor invite emails. Invites are still created without these — only the email send is skipped.
SkyTime includes Docker-based Postgres backups. Backups are written as custom-format pg_dump files so they can be restored with pg_restore.
Create a local file-backed backup:
docker compose --profile backup run --rm backup-localThe dump and .sha256 checksum are persisted under ./backups, which is ignored by git except for the placeholder directory.
For S3-compatible storage, copy the example env file and fill in your bucket, endpoint, and credentials:
cp .env.backup.example .env.backupThen run:
docker compose --env-file .env.backup --profile backup-s3 run --rm backup-s3S3_ENDPOINT_URL supports S3-compatible providers such as MinIO, Cloudflare R2, Backblaze B2, and Wasabi. Leave it empty for AWS S3. S3_FORCE_PATH_STYLE=true is useful for MinIO and many self-hosted S3-compatible services.
Restore from a local backup file:
RESTORE_FILE=skytime-skytime-20260511T010000Z.dump \
CONFIRM_RESTORE=true \
docker compose --profile restore run --rm restoreRestore directly from S3-compatible storage:
RESTORE_FILE=s3://your-bucket/skytime/postgres/skytime-skytime-20260511T010000Z.dump \
CONFIRM_RESTORE=true \
docker compose --env-file .env.backup --profile restore run --rm restoreRestores replace objects in the configured Postgres database. Stop the app process before restoring into a live environment.
Regenerate the seeded demo account, organization, projects, tasks, timesheets, and screenshots with:
cd src/client/next-landing-page
npm run demo:screenshotsThe script signs up fictional users, creates fictional organizations, adds sample projects, tasks, and time entries, then captures light and dark mode screenshots into docs/screenshots.
| View | Light | Dark |
|---|---|---|
| Dashboard | ![]() |
![]() |
| Projects | ![]() |
![]() |
| Board | ![]() |
![]() |
| Timesheets | ![]() |
![]() |
| Settings | ![]() |
![]() |
| View | Light | Dark |
|---|---|---|
| Dashboard | ![]() |
![]() |
| Projects | ![]() |
![]() |
| Board | ![]() |
![]() |
| Timesheets | ![]() |
![]() |
| Settings | ![]() |
![]() |



















