Libre311 is an open-source web application for managing municipal service requests, built on the Open311 GeoReport v2 specification.
Open311 is a standardized protocol for civic issue tracking, enabling residents to report non-emergency problems (potholes, broken streetlights, graffiti, etc.) to their local government. Libre311 provides a complete implementation of this standard, including:
- Public Interface - Citizens can submit service requests with location, photos, and details
- Multiple Views - Browse issues via interactive map, searchable list, or data table
- Admin Dashboard - Configure service types, manage jurisdictions, and track request status
- REST API - Open311-compliant endpoints for integration with other civic tech tools
| Component | Technology |
|---|---|
| Backend API | Java 25 / Micronaut 4.x |
| Frontend | SvelteKit / TypeScript / Tailwind CSS |
| Database | MySQL or PostgreSQL |
| Authentication | OAuth via UnityAuth service |
| Maps | Leaflet with Nominatim geocoding |
| Software | Version | Notes |
|---|---|---|
| Java JDK | 25 | Eclipse Temurin recommended. Use SDKMAN for easy installation. |
| Node.js | 18+ | Required for frontend development |
| npm | 9+ | Comes with Node.js |
| Docker | 20+ | Optional, for containerized development |
| Docker Compose | 2.0+ | Optional, for containerized development |
UnityAuth - Libre311 uses UnityAuth for authentication and authorization. You must have UnityAuth running before starting Libre311.
- Clone the UnityAuth repository
- Follow its setup instructions to run locally or via Docker
- Note the UnityAuth URL for configuring Libre311
- Google Cloud Platform - For image storage (Cloud Storage) and content moderation (SafeSearch API)
- MySQL or PostgreSQL - Can use Docker-provided database for development
-
Configure environment variables
cp setenv.sh.example setenv.sh # Edit setenv.sh with your configuration (database, auth URLs, etc.) -
Start the API server (from project root)
source setenv.sh ./gradlew app:runAPI will be available at http://localhost:8080
-
Start the frontend (in a new terminal)
source setenv.sh cd frontend npm install npm run dev
UI will be available at http://localhost:3000
See app/README.md and frontend/README.md for detailed configuration options.
+------------+ +---------+ +----------+
| Web App UI |<--->| Web API |<-+->| Database |
+------------+ +---------+ | +----------+
| +---------------+
+->| Auth Provider |
| +---------------+
| +------------------+
+->| Geocoding Service|
| +------------------+
| +---------------+
+->| Object Storage |
+---------------+
| Component | Description |
|---|---|
| Web App UI | SvelteKit application served independently or by the API |
| Web API | Micronaut REST API (horizontally scalable) |
| Database | MySQL or PostgreSQL with spatial extensions |
| Auth Provider | UnityAuth service for OAuth/JWT authentication |
| Geocoding Service | Nominatim (default) for reverse geocoding |
| Object Storage | GCP, AWS, or Azure for user-uploaded images |
Libre311/
├── app/ # Backend API (Java/Micronaut)
│ └── src/main/java/app/
│ ├── RootController.java # Open311 GeoReport API endpoints
│ ├── JurisdictionAdminController.java
│ ├── ImageStorageController.java
│ ├── dto/ # Request/response objects
│ ├── model/ # JPA entities (Service, ServiceRequest, etc.)
│ ├── service/ # Business logic layer
│ ├── security/ # OAuth/JWT authentication
│ └── geocode/ # Geocoding provider abstraction
│
├── frontend/ # Frontend UI (SvelteKit)
│ └── src/
│ ├── routes/
│ │ ├── issues/ # Browse service requests (map, list, table)
│ │ ├── issue/create/ # Submit new service request
│ │ ├── groups/ # Admin: manage service types
│ │ ├── policies/ # Terms of use, privacy policy
│ │ └── login/ # Authentication
│ └── lib/
│ ├── components/ # Reusable UI components
│ └── context/ # Svelte context providers
│
├── docker-compose.local.yml # Local Docker environment
└── setenv.sh.example # Environment variable template
| Model | Description |
|---|---|
Service |
Type of issue (e.g., "Pothole", "Streetlight Out") |
ServiceDefinition |
Attributes/subtypes for a service |
ServiceRequest |
A reported issue with location, description, status |
Jurisdiction |
Geographic/administrative boundary for services |
User |
Authorized admin user |
Backend (Java)
# Run all tests
./gradlew app:test
# Run specific test class
./gradlew app:test --tests app.RootControllerTest
# Run with continuous rebuild
./gradlew app:test -tFrontend (SvelteKit)
cd frontend
# Run all tests (unit + integration)
npm run test
# Run unit tests only
npm run test:unit
# Run integration tests (Playwright)
npm run test:integrationFrontend
cd frontend
npm run lint # Check for linting errors
npm run format # Auto-format code with Prettier
npm run check # TypeScript type checkingBackend
./gradlew app:assemble
# Output: app/build/libs/app-*-all.jarFrontend
cd frontend
npm run build # Production build
npm run preview # Preview production build locally-
Configure environment files
cp .env.example .env.docker cp frontend/.env.example frontend/.env.docker # Edit both files with your configuration -
Configure GCP credentials (if using image uploads)
# Linux/macOS export ADC_PATH=$HOME/.config/gcloud/application_default_credentials.json # Windows set ADC_PATH=%APPDATA%\gcloud\application_default_credentials.json
See app/README.md for details on Application Default Credentials.
-
Add hosts entries (recommended)
# Add to /etc/hosts 127.0.0.1 libre311-api 127.0.0.1 libre311-db 127.0.0.1 libre311-ui 127.0.0.1 libre311-ui-dev
# Start all services
docker compose -f docker-compose.local.yml up
# Or use Makefile shortcuts:
make compose_api # API + database only
make compose_ui # UI + database only
make compose_all # All servicesService URLs:
| Service | Host URL | Docker Internal |
|---|---|---|
| API | http://localhost:8080 | http://libre311-api:8080 |
| UI | http://localhost:3000 | http://libre311-ui-dev:3000 |
| MySQL | localhost:23306 | libre311-db:3306 |
After code changes, rebuild the affected images:
docker compose -f docker-compose.local.yml build libre311-api
docker compose -f docker-compose.local.yml build libre311-ui-dev
docker compose -f docker-compose.local.yml upAs outlined in Open311's Service Discovery page, an endpoint is offered
at /discovery which describes organization contact and the base URLs of endpoints.
As a convenience, Libre311 provides a default set of endpoint configurations for a set
of production and test environments as well as the ability to set
configuration values via the following environment variables:
LIBRE311_DISCOVERY_CHANGESET_DATETIMELIBRE311_DISCOVERY_CONTACT_MESSAGELIBRE311_DISCOVERY_PRODUCTION_URLLIBRE311_DISCOVERY_TEST_URL
Please feel free to modify app/src/main/resources/application.yml's app.discovery
content to your use case.
The following environment variables should be set to configure the application:
Database:
LIBRE311_JDBC_URL- JDBC connection URLLIBRE311_JDBC_DRIVER- JDBC driver classLIBRE311_JDBC_USER- Database usernameLIBRE311_JDBC_PASSWORD- Database passwordLIBRE311_AUTO_SCHEMA_GEN- Schema generation mode (updatefor development)
Object Storage:
GCP_PROJECT_ID- The GCP project IDSTORAGE_BUCKET_ID- The ID of the bucket where user-uploaded images are hosted
Security:
RECAPTCHA_SECRET- Site abuse preventionMICRONAUT_SECURITY_TOKEN_JWT_SIGNATURES_SECRET_GENERATOR_SECRET- Secret used to sign JWTsMICRONAUT_SECURITY_TOKEN_JWT_GENERATOR_REFRESH_TOKEN_SECRET- Secret for JWT renewal tokensMICRONAUT_SECURITY_REDIRECT_LOGIN_SUCCESSMICRONAUT_SECURITY_REDIRECT_LOGIN_FAILUREMICRONAUT_SECURITY_REDIRECT_LOGOUT
Authentication (UnityAuth):
AUTH_BASE_URL- UnityAuth service base URLAUTH_JWKS- UnityAuth JWKS endpoint for JWT validation
Geocoding (optional, has sensible defaults):
NOMINATIM_URL- Geocoding service URL (default:https://nominatim.openstreetmap.org)GEOCODING_PROVIDER- Provider selection (default:nominatim)
The Web Application UI requires the URL of the API when built.
This is set using the VITE_BACKEND_URL environment variable.
If the Web API will serve the UI, then set VITE_BACKEND_URL to /api.
Set the following environment variables to enable Google as an auth provider:
GOOGLE_CLIENT_ID- The id of OAuth client.GOOGLE_CLIENT_SECRET- The secret of the OAuth client.MICRONAUT_SECURITY_OAUTH2_CLIENTS_GOOGLE_OPENID_ISSUER- Set to "https://accounts.google.com"
See app/src/main/resources/application.yml for a complete list of configuration options.
Copyright 2023-2025 Libre311 Authors. Licensed under the Apache License 2.0.