- Python 42%
- Svelte 24%
- CSS 23.9%
- TypeScript 6.9%
- Just 1.9%
- Other 1.2%
| apps | ||
| data/fixtures | ||
| docs/dev-docs | ||
| example-data | ||
| examples | ||
| infra | ||
| .dockerignore | ||
| .gitignore | ||
| AGENTS.md | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| justfile | ||
| package.json | ||
| plan.md | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| todo.md | ||
Open Climbing
Open Climbing is an open-source, open-data platform for discovering climbing areas, browsing routes, and viewing interactive photographic topos. It is designed as a mobile-first web application with explicit offline area downloads.
The project is in early development. The backend catalog API and first mobile-first browsing interface are implemented.
Planned features
- Area and sector discovery
- Route browsing with grade and discipline filters
- Interactive photographic topos
- Map-based search
- Image licensing and attribution
- Offline area downloads
- Curated open-data imports
See plan.md for the architecture, milestones, and complete MVP scope. Technical notes are indexed under docs/dev-docs/.
Deploy with Coolify
The root Dockerfile builds the SvelteKit frontend and FastAPI backend into one production image. In Coolify, create a Dockerfile application with port 8000, health check path /health, and this required runtime variable:
BASE_URL=https://dev.climbs.example.com
OPEN_CLIMBING_DATABASE_URL=postgresql+psycopg://user:password@host:5432/database
OPEN_CLIMBING_ENVIRONMENT=development
Deploy develop to the development environment and main to production. BASE_URL supplies the public origin; its hostname is allowed automatically alongside localhost and 127.0.0.1, so a separate allowed-hosts variable is not needed. The container waits up to 60 seconds for PostgreSQL and applies Alembic migrations before accepting traffic. OPEN_CLIMBING_SECRET_KEY is not required until authentication or signed sessions are implemented. See docs/dev-docs/coolify.md for the complete environment isolation, branch, backup, and promotion strategy.
Coolify-generated postgres:// and postgresql:// connection strings are accepted and normalized to the psycopg driver automatically. Keep database credentials available at runtime only; do not expose them as Docker build variables.
The footer displays the release version from the root package.json and the first eight characters of the deployed commit. Enable Include Source Commit in Build in Coolify so its predefined SOURCE_COMMIT build argument is embedded in the static frontend. Local Docker builds add the current Git commit automatically.
Test the production image locally with an isolated PostGIS container:
just docker-local-up
just docker-local-health
just docker-local-logs
just docker-local-down
The local recipes use sudo docker by default and publish the application at http://localhost:8811. Set DOCKER=docker when the current user already has Docker socket access.
Technology
Backend
- Python
- FastAPI
- SQLAlchemy 2
- Alembic
- PostgreSQL with PostGIS
- Pydantic
uv, pytest, Ruff, and ty
Frontend
- SvelteKit and Svelte 5
- TypeScript
- Tailwind CSS and shadcn-svelte
- MapLibre GL JS
- IndexedDB and service workers
Repository layout
apps/api/ FastAPI application and tests
apps/web/ SvelteKit catalog frontend
infra/compose.yaml Local PostgreSQL/PostGIS and API services
justfile Development commands
plan.md Product and implementation plan
docs/dev-docs/ Technical and deployment documentation
Getting started
Prerequisites
- Docker with Docker Compose
uvfor local backend developmentpnpmfor frontend developmentjustfor the convenience commands (optional)
Run with Docker Compose
docker compose -f infra/compose.yaml up --build
In another terminal, apply the database migration:
cd apps/api
uv run alembic upgrade head
The API is available at:
- API: http://localhost:8000
- Health: http://localhost:8000/health
- Swagger UI: http://localhost:8000/docs
- OpenAPI schema: http://localhost:8000/openapi.json
Using just, the equivalent commands are:
just infra-up
just api-migrate
Run the API locally
Start PostgreSQL first:
docker compose -f infra/compose.yaml up -d postgres
Then install dependencies, migrate, and launch FastAPI:
cd apps/api
uv sync --dev
uv run alembic upgrade head
uv run fastapi dev src/open_climbing_api/main.py
Configuration uses environment variables prefixed with OPEN_CLIMBING_. The default database URL connects to the local Compose PostgreSQL service exposed on port 5432.
Run the frontend locally
With the API running on port 8000:
pnpm install
cp apps/web/.env.example apps/web/.env
pnpm --dir apps/web dev
The web application is available at http://localhost:5173 and listens on all interfaces for LAN or Tailscale access. Set API_PROXY_TARGET in apps/web/.env if the API uses another port.
For a single-origin build, run just web-build and restart FastAPI. FastAPI will serve the generated frontend at / while retaining the API under /api/v1.
Run an isolated branch preview
The development Compose stack is separate from the default catalog stack. It uses its own PostGIS volume, exposes PostgreSQL on port 5433, migrates and loads the sample catalog automatically, and serves the production frontend and API from the same origin on port 8125.
just dev-infra-up
Open http://localhost:8125. Follow startup with just dev-infra-logs, stop it with just dev-infra-down, or remove its database volume with just dev-infra-reset.
Override the host ports when needed:
OPEN_CLIMBING_DEV_PORT=9125 OPEN_CLIMBING_DEV_DB_PORT=6433 just dev-infra-up
Apple containers on macOS
On Apple silicon with macOS 26 or newer, the same isolated preview can run with Apple's container CLI instead of Docker. The recipe starts the container system when necessary and creates a dedicated network, PostGIS volume, database container, and API container.
just mac-dev-infra-up
just mac-dev-infra-logs
Stop the containers with just mac-dev-infra-down. Use just mac-dev-infra-reset to also delete the development database volume and network. OPEN_CLIMBING_DEV_PORT and OPEN_CLIMBING_DEV_DB_PORT provide the same host-port overrides as the Docker recipe.
Development commands
just api-dev # Run the development API
just api-test # Run backend tests
just api-lint # Run Ruff checks
just api-format # Format backend code
just api-migrate # Apply Alembic migrations
just web-dev # Run the SvelteKit frontend on all interfaces
just web-build # Build the frontend for FastAPI to serve
just web-check # Check, test, and build the frontend
just infra-up # Start local services
just infra-down # Stop local services
just dev-infra-up # Build and run an isolated preview on port 8125
just dev-infra-logs # Follow the branch preview API logs
just dev-infra-down # Stop the isolated branch preview
just dev-infra-reset # Stop it and delete its development database
just mac-dev-infra-up # Run the preview with Apple's container CLI
just mac-dev-infra-logs # Follow the Apple container API logs
just mac-dev-infra-down # Stop the Apple containers
just mac-dev-infra-reset # Stop them and delete the development database
Without just, run the corresponding uv commands from apps/api.
Quality checks
cd apps/api
uv run ruff check .
uv run ruff format --check .
uv run ty check src tests
uv run pytest
Current application
The frontend currently supports area discovery, crag details, sector selection, and route name/grade filtering. It consumes the typed catalog resources under /api/v1, including regions, countries, crags, sectors, and routes.
Contributing
The contribution workflow is still being established. For now, open an issue or submit a focused pull request that includes tests and passes the quality checks above.