A shared calendar app for running one calendar across multiple people — families, teams, talent agencies. It blends TimeTree's shared-calendar simplicity with a Google Calendar–style permission model. Monorepo: a Go REST API plus a React SPA.
Stack: Go 1.25 · Huma v2 · MySQL 8.4 · React 19 · Bun
| Monthly view | Weekly view |
|---|---|
![]() |
![]() |
| Share & embed | |
![]() |
Responsive mobile layout (vertical month scroll):
- Shared calendars — per-member roles (owner / editor / viewer), invite links, public share tokens, and an embeddable (embed) view
- Events — all-day & timed, recurrence (RRULE) with per-occurrence exception editing, drag to move/resize, attendees, checklists, attachments, comments
- Memos — per-calendar notes
- Albums — per-calendar photo albums (optionally linked to events)
- Activity & audit history — change history for events/memos plus a per-calendar activity feed
- Auth — email + password, Google / LINE OAuth/OIDC, password reset, and a development-only password-less login
- Admin — OAuth provider configuration and an allowlist of permitted sign-in emails/domains
- Export / import — iCal and CSV
- i18n & themes — English / Japanese locales and theme switching
| Area | Technology |
|---|---|
| API | Go 1.25 + Huma v2 (OpenAPI) + chi/v5 + sqlc |
| DB | MySQL 8.4 (Docker Compose) |
| Object storage | S3-compatible (MinIO locally) |
| Web | React 19 + TypeScript + Vite + TanStack Router + Zustand + Tailwind CSS 4 + Luxon |
| Lint / format | Biome (web) / gofmt (Go) |
| Package manager | Bun |
| Testing | Go E2E (testify + real MySQL) / Vitest (web) |
apps/
api/ Go REST API
cmd/api/ entrypoint
cmd/createuser/ user-creation CLI
internal/ config, auth, audit, cleanup, db/generated,
errors, mailer, recurrence, secrets, storage,
http/{handlers,middleware,router}
tests/e2e/ E2E tests
web/ React SPA
src/components/ UI components
src/routes/ TanStack Router (file-based; incl. share/ and embed/)
src/stores/ Zustand (auth / calendar / ui stores)
src/lib/ utilities (date, recurrence, upload, theme, …)
src/i18n/ English / Japanese locales
sql/
tables/ table definitions (001-017)
queries/ sqlc query definitions
schema.sql combined schema produced by build-schema.sh
sqlc.yaml sqlc config
compose.yml local stack: MySQL + MinIO
bun install # install deps (Bun required)
make db-up # start MySQL (compose.yml)
make db-apply # build & apply the schema
make db-seed-users # create demo@example.com / admin@example.com
make dev # start DB + API + web together- API: http://localhost:8080 (
/health; OpenAPI is generated by Huma) - Web: http://localhost:5173
In development (TC_ENV=development), /auth/dev-login provides password-less
login for the seeded accounts.
make dev # DB + API + web in parallel
make db-up / db-down # start / stop MySQL
make db-apply # apply schema (build-schema.sh → schema.sql)
make db-seed # schema + users + sample data
make minio-up # start MinIO (S3-compatible storage)
make sqlc # generate sqlc code
make api / make web # run individually
make build-api # build the API binary (bin/api)
make create-user ARGS="-email a@b.com -password secret123 -admin"
make format # gofmt + Biome autofix
make lint # gofmt check + Biome (no writes)
make test-api # Go unit tests
make test-e2e # E2E (requires MySQL + TC_TEST_INTEGRATION=1)
make test-e2e-storage # E2E including MinIO-backed storage tests
bun run check # Biome check
bun run typecheck # TypeScript typecheck (tsc -b)
cd apps/web && bun run test # web unit tests (Vitest)All prefixed with TC_. Key ones below; see apps/api/internal/config/config.go
for the full list and defaults.
| Variable | Purpose | Default |
|---|---|---|
TC_ENV |
development enables dev conveniences |
production |
TC_PORT |
API port | 8080 |
TC_DB_DSN |
MySQL DSN | ttuser:ttpw@tcp(127.0.0.1:33306)/timetree_clone?parseTime=true |
TC_JWT_SECRET |
JWT signing key (≥32 bytes required in prod) | dev fallback |
TC_PASSWORD_LOGIN_ENABLED |
enable email+password auth | true |
TC_CORS_ALLOWED_ORIGINS |
allowed CORS origins (no wildcard in prod) | localhost:5173 |
TC_WEB_URL / TC_API_PUBLIC_URL |
public URLs for web / API | localhost |
TC_S3_ENDPOINT / TC_S3_ACCESS_KEY / TC_S3_SECRET_KEY / TC_S3_BUCKET / TC_S3_USE_SSL |
S3-compatible storage | MinIO defaults |
TC_SMTP_HOST and other TC_SMTP_* |
mail delivery (stdout when unset; required in prod) | console output |
TC_GOOGLE_CLIENT_ID / TC_GOOGLE_CLIENT_SECRET |
Google OAuth | empty |
TC_GOOGLE_ALLOWED_DOMAINS |
allowed Google sign-in domains (comma-separated) | unrestricted |
TC_LINE_CLIENT_ID / TC_LINE_CLIENT_SECRET |
LINE OAuth | empty |
TC_SECRETS_KEY |
key encrypting secrets stored in the DB (hex/base64, 32 bytes) | empty |
In production,
Config.Validate()refuses to start with the default JWT secret, default MinIO credentials, wildcard CORS, or an unset SMTP host.
DB / storage (compose.yml): TC_DB_PORT (default 33306), TC_DB_ROOT_PASSWORD,
TC_DB_NAME, TC_DB_USER, TC_DB_PASSWORD, TC_MINIO_API_PORT,
TC_MINIO_CONSOLE_PORT.
Huma v2 generates the OpenAPI spec automatically. Routes live in
apps/api/internal/http/router/router.go.
- Public —
/health,/auth/*(register/login/dev-login/password-reset/oauth),/share/{token}(public calendar) - Authenticated —
/user,/calendars, and/calendars/{id}/{events,memos,members,invites,albums,labels,activity,export,import} - Admin —
/admin/oauth-providers,/admin/allowed-emails
Unauthenticated endpoints are rate-limited per client IP.
E2E tests create an independent user + calendar per test (tenant isolation) and run in parallel against a real MySQL.
TC_DB_PORT=3307 make db-up # start MySQL on the same port CI uses
make test-e2e # runs with TC_TEST_INTEGRATION=1CI (.github/workflows/ci.yml) runs web (Biome / typecheck / Vitest) and API
(build / vet / E2E).
- The schema is layered.
sql/core/is a copy of the shared schema contract that lets more than one product write to the same database — it is not edited here and is pinned upstream insql/core/UPSTREAM.json.sql/time/holds this application's own tables. sql/build-schema.shconcatenates core then time intosql/schema.sql, whichmake db-applyapplies.make test-conformancechecks the resulting schema against the contract.make check-corechecks that the vendored copy is unedited and current.- Write queries in
sql/queries/*.sqland runmake sqlcto generate Go code intoapps/api/internal/db/generated/.
AGPL-3.0 — self-host, modify, and redistribute freely. Network use requires source availability.
This is an independent, original project. It is inspired by the shared-calendar experience of TimeTree, but the code, UI, and UX are its own — no source, assets, or designs are copied from any third party.
"TimeTree", "Google Calendar", and other product names are trademarks of their respective owners and are used here only for descriptive reference. This project is not affiliated with, endorsed by, or sponsored by any of them.



