Architecture¶
Overall architecture¶
GeoVault is a monolithic Go HTTP service that combines:
- Server-rendered management UI (
web/) - JSON REST-style API routes registered in
main.go - PostgreSQL metadata + Central authentication
- Filesystem dataset versions
- Out-of-process GDAL/OGR tooling
- HTTP integrations with sibling federation services
There is one listener on :8085. The version catalog is a process-global slice and each save replaces the complete PostgreSQL table or JSON file. There is no application lock around concurrent mutations and no cross-process coordination; safe multi-process writes are therefore unsupported by the reviewed implementation.
flowchart TB
subgraph ingress
AP[Apache /geovault]
end
subgraph process [GeoVault process]
MW[Forwarded proto + login + service ACL]
R[Gorilla Mux routes]
API[internal/api handlers]
ST[store.Versions]
ED[internal/editor]
MW --> R --> API
API --> ST
API --> ED
end
AP --> MW
API --> DB[(Meta PostgreSQL)]
MW --> AUTH[(Central auth DB)]
API --> AUTH
API --> FS[(/data/projects)]
API --> GDAL[GDAL/OGR CLI]
API --> EXT[QGIS / TileServer / PostGIS Service / GeoServer / S3 / ESRI]
Major components¶
| Component | Package / path | Responsibility |
|---|---|---|
| Entrypoint | main.go |
Open DB/auth, load catalog, reconcile ACLs, register routes, listen |
| HTTP API | internal/api |
All JSON/multipart endpoints and HTML-adjacent handlers |
| Auth adapter | internal/authstore |
Read sessions/users/groups; register resources and manage ACL rows |
| Shared auth | ../shared/auth |
Cookie/Bearer resolution, middleware, ACL helpers |
| Metadata DB | internal/db |
Migrations, datasets, folders, connections, GeoServer tables |
| Version store | internal/store |
Global Versions slice; whole-catalog load/save |
| Editor | internal/editor |
Row-level edits for OGR files / PostGIS |
| GeoServer client | internal/services/geoserver |
REST + secret encryption |
| PostGIS registry | internal/postgisregistry |
Proxy to PostGIS Service |
| Meta extraction | internal/meta |
GDAL/OGR info → JSON |
| UI | web/ + shared/ui |
Templates and static assets |
Directory layout (runtime data)¶
| Path | Purpose |
|---|---|
/data/projects/{projectId}/{dataset}/v{N}/ |
Root-scoped file versions |
/data/projects/{projectId}/folders/{folderId}/{dataset}/v{N}/ |
Folder-scoped file versions |
GEOVAULT_VERSIONS_JSON, {GEOVAULT_DATA_DIR}/versions.json, or data/versions.json |
JSON catalog precedence when no metadata DB |
data/metadata/{dataset}_v{N}.json |
Cached extracted metadata |
| PostgreSQL tables | Registry, jobs, connections, folders (see Database) |
The only project initialized by main is in-memory project 1 (Default, type spatial). Project persistence or arbitrary project creation is not implemented in the reviewed schema.
Request flow¶
sequenceDiagram
participant C as Client
participant P as Reverse proxy
participant G as GeoVault
participant A as Central auth DB
participant H as Handler
C->>P: HTTPS /geovault/...
P->>G: HTTP :8085 + X-Forwarded-*
G->>G: ApplyForwardedProto
G->>A: Resolve acugis_session
alt unauthenticated browser
G-->>C: 303 /login?next=...
else unauthenticated API
G-->>C: 401
end
G->>A: Check service/geovault permission
G->>H: Route handler
H->>A: optional geovault_dataset permission
H-->>C: JSON / file / HTML / redirect
Global middleware order (main.go):
web.ApplyForwardedProtoapi.RequireLoginapi.RequireGeoVaultServiceAccess
Service ACL permission selection (geovaultServiceRequiredPermission):
| Condition | Required permission |
|---|---|
/connections*, /api/connections*, /api/database-services* |
admin |
GET /upload or POST paths containing /upload |
edit |
| Non-GET/HEAD methods | edit |
| Otherwise | view |
Many dataset routes additionally require geovault_dataset view, edit, or admin. Administrators bypass these checks. Permission inheritance is admin → edit → view, and the shared provider adds the Public group during checks.
Authentication flow¶
See Authentication. In short:
- Primary: Central
acugis_sessioncookie;shared/authhashes the cookie value and looks it up indashboard_sessions - Secondary attempt:
Authorization: Beareris parsed byshared/auth - GeoVault constructs its provider with
NewAuthorizationProvider(...)and never callsWithTokens, so it supplies noTokenStore; PAT-only requests are treated as unauthenticated - Login/logout routes are not implemented in GeoVault; Central owns them
- The auth pool is mandatory. It uses
GEOVAULT_AUTH_DATABASE_URL, thenCENTRAL_DATABASE_URL, thenDATABASE_URL
Database interactions¶
| Concern | Mechanism |
|---|---|
| Open metadata | db.OpenMeta() on DATABASE_URL; optional |
| Schema | Idempotent SQL in internal/db/migrate.go |
| Versions | Full replace of versions table on save when Meta is set |
| Auth | Separate mandatory pool from authstore.OpenFromEnv() |
| Remote PostGIS browse/edit | Short-lived pools via internal/pgconn (MaxOpenConns=5) |
When DATABASE_URL is empty, GeoVault can still start only if one of the two dedicated auth DSNs is set. Version persistence falls back to JSON; metadata-dependent folders, registries, jobs, connections, archive state, and related APIs are unavailable or degraded. Exact status codes vary by handler and should not be generalized.
Background workers¶
There is no scheduler, cron, or durable worker process.
| Work | Trigger | Execution | Persistence |
|---|---|---|---|
| GeoServer publish | Publish API or upload sidecar | In-process goroutine | geoserver_jobs |
| ESRI import | POST /api/esri/import |
In-process goroutine | geoserver_jobs (job_type=esri_import) |
| QGIS snapshot count enrichment | List projects | Fan-out goroutines, then wait | None |
| GeoServer remote reconcile | Startup | Synchronous in main |
Version catalog |
| Dataset ACL reconcile | Startup | Synchronous in main |
Central resources |
No startup code scans and resumes jobs. A restart can leave rows queued or running; retry, deduplication, cancellation, retention, and concurrency limits are not established.
Caching¶
| Kind | Present? |
|---|---|
| Redis / Memcached | No |
| HTTP ETag / explicit API cache policy | Not found in reviewed handlers |
| Metadata JSON files | Persistent derived cache of GDAL output |
| Browser caches | sessionStorage / localStorage / in-memory Maps in web/app.js |
File storage¶
- Uploads and local imports materialize files under
/data/projects/... - Shapefile ZIPs are validated and extracted in the version directory
- Remote S3 mode can register a version without a local file (
datasource_mode=remote) - The registered download handler serves only a non-empty local
file_path; remote S3 and live PostGIS versions are not exported by that route - Metadata cache paths are keyed only by dataset name and version, not folder ID; collision behavior for same-name datasets in different folders is not resolved by the path format
External integrations¶
| System | Configuration | Role |
|---|---|---|
| Central | Auth DSNs | Sessions, groups, ACLs |
| QGIS Publish | QGIS_PUBLISH_URL / QGIS_PUBLISH_BASE_URL |
Raster publish; project list/restore/download proxy |
| TileServer | TILESERVER_REGISTER_URL |
Vector publish registration |
| PostGIS Service | POSTGIS_SERVICE_URL |
Discover/adopt/create databases; schemas/tables |
| GeoServer | Per-connection URL + credentials | Catalog, publish, styles, workspaces |
| HTTP(S) | Import timeout/size env | URL ingest |
| S3-compatible | Per-request credentials | Object import or remote registration |
| ArcGIS | Per-request service URL | Browse layers; import jobs |
Caller cookies are forwarded to some internal services (QGIS Publish, TileServer, PostGIS Service) so the remote service can authorize the user.
GeoVault also directly reads PostGIS Service registry tables (postgis_connections, postgis_databases, postgis_jobs) when they coexist in the metadata database. Those tables are externally owned and are never created by GeoVault. Remote spatial databases are queried through information_schema, PostgreSQL catalogs, and PostGIS geometry_columns; those are not GeoVault schema.
Publish flow¶
sequenceDiagram
participant C as Client
participant G as GeoVault
participant D as Disk / catalog
participant T as TileServer
participant Q as QGIS Publish
participant S as GeoServer
C->>G: POST publish
G->>D: Resolve version and ACL
alt vector legacy publish
G->>T: Register local path
T-->>G: Endpoint
else raster legacy publish
G->>Q: Multipart raster + caller auth
Q-->>G: Project path
else GeoServer publish
G->>D: Insert geoserver_jobs row
G-->>C: Job id
G-)S: In-process asynchronous REST work
G->>D: Update job/publication rows
end
UI composition¶
Templates parse page HTML plus shared partials (sidebar, shell-head, federation sidebar, PostGIS/GeoServer panels). Static files under ./web/ are served last via http.FileServer. Shared federation shell assets mount at /shared/ui/.