Skip to content

Architecture

GeoSync is a server-rendered PHP control plane around PostgreSQL metadata and host-managed synchronization workers. It is not a single self-contained service: normal operation depends on systemd, filesystem configuration, Python/CLI tools, and optional sibling services.

Component view

flowchart LR
  B[Browser] --> W[Apache + PHP GeoSync]
  W --> M[(GeoSync PostgreSQL)]
  W --> C[(Central metadata DB)]
  W --> F[FIFO backend]
  F --> S[systemd]
  S --> Q[QField worker]
  S --> D[Mergin DB Sync]
  S --> A[ArcGIS worker]
  Q & D & A --> P[(Target PostGIS)]
  Q & D & A --> FS[Project files]

PHP pages under admin/ render views; admin/action/*.php accept form requests, perform session/ownership checks, update metadata/configuration, and write backend commands. Domain classes use the procedural PostgreSQL extension directly; there is no framework, router, ORM, migration runner, or dependency-injection layer.

Request flow

sequenceDiagram
  participant U as Browser
  participant A as Auth prepend
  participant P as PHP page/action
  participant DB as GeoSync DB
  participant B as Backend FIFO
  U->>A: HTTP request + session cookie
  A->>A: Resolve mode and authorize
  A->>P: Hydrated legacy session
  P->>DB: Read/write records
  opt service operation
    P->>B: op type project-id
    B-->>P: FIFO write completes
  end
  P-->>U: HTML, JSON, or file

The proxy endpoint api/proxy.php additionally validates an exact service/method/path allow-list, handles selected analysis operations in PHP, or forwards JSON with X-User-ID. The generic analysis and FormBuilder proxies require a recognized session but do not forward all request headers/methods.

Authentication and authorization

flowchart TD
  R[Request] --> M{Auth mode}
  M -->|central| K[Read acugis_session]
  K --> D{Central DSN works?}
  D -->|yes| L[Hash token and query sessions/users/groups]
  D -->|no or no match| API[GET Central /api/me]
  L & API --> ADM{Platform admin?}
  ADM -->|yes| H[Hydrate synthetic GeoSync Admin]
  ADM -->|no| X[403]
  M -->|local| LS[Legacy PHP session]
  M -->|keycloak| O[Legacy OIDC session/roles]
  H & LS & O --> APP[Page/action checks]

Current Central integration is administrator-only. A service ACL helper and bridge/JIT-provisioning implementation exist, but the main prepend does not call them. Public Central paths are limited to login/logout/signup and legacy login/signup/verification actions.

Database model

erDiagram
  USER ||--o{ SERVER : owns
  USER ||--o{ PGLINK : owns
  USER ||--o{ PROJECT : owns
  USER ||--o{ RSYNC : owns
  USER ||--o{ GEONODE : owns
  USER ||--o{ GROUP : owns
  USER ||--o{ GSTORE : owns
  SERVER ||--o{ PROJECT : hosts
  PGLINK ||--o{ PROJECT : targets
  PROJECT ||--o{ PROJECT_RSYNC : uses
  RSYNC ||--o| PROJECT_RSYNC : attached
  PROJECT ||--o{ GSTORE : publishes
  PROJECT o|--o{ RSYNC : local_target
  USER ||--o| GEOSYNC_USER_BRIDGE : maps

The base schema also has signup. Optional publish columns live on project. The bridge table and user.enabled/auth_source are optional SQL. sync_secrets is referenced by PHP but has no supplied DDL. Central tables (dashboard_sessions, dashboard_users, auth groups, resources, permissions) belong to the sibling platform database.

Worker flow

flowchart TD
  UI[Admin action] --> CFG[Write project INI/YAML]
  UI --> FIFO[Write backend FIFO]
  FIFO --> SD[systemctl template instance]
  SD --> T{Project type}
  T -->|qf| Q[Poll/download QField files]
  T -->|gdb| G[Download GDB and ogr2ogr to GPKG]
  T -->|db| D[Patched Mergin DB Sync]
  Q & G --> GD[geodiff copy/diff/apply]
  GD & D --> PG[(Target PostGIS)]
  Q & D --> IMG[Copy image files]
  Q & D --> RS[Run attached rsync scripts]

Workers log to /var/log/qz_sync<id>.log; the backend logs to /var/log/qz_sync_backend.log in supplied units. single_run, sleep_time, initialization flags, and QField retention are configuration-driven.

There are no cron jobs, systemd timer units, or separate queue workers in the repository. Recurring synchronization is implemented inside the long-running QField/ArcGIS loops with sleep_time, and inside the external Mergin DB Sync daemon. Optional rsync work runs after a synchronization cycle; it is not independently scheduled.

Cache and storage

flowchart LR
  S[PHP session] --> PS[PHP session handler]
  C[Generated config] --> QC[/etc/quartz-sync.d]
  W[Worker data] --> QD[QZ_DATA]
  QD --> GP[Current and retained GeoPackages]
  QC --> RL[Rsync scripts/logs/PIDs/excludes]
  QD --> TMP[Diffs and temporary downloads]
  PUB[QGIS publishing] --> PR[PROJECTS_ROOT]

No Redis, Memcached, application cache abstraction, or explicit HTTP response cache exists. The effective cache is filesystem state: downloaded projects, retained QField snapshots, metadata files, and PHP sessions. Analysis static overrides are sent with Cache-Control: no-cache.

Storage locations vary by worker:

  • QField/ArcGIS: QZ_HOME/data/<project-id>; default QZ_HOME/data equals QZ_DATA.
  • Mergin DB Sync: external worker paths under QZ_DATA/dbsync; source contains both project-name and proj_<id> assumptions, so verify the installed DB Sync patch/version.
  • Rsync support: QZ_CONF/rsync.d.
  • Published QGIS projects: QGIS_PUBLISH_PROJECTS_ROOT/<publisher-id>.

Integrations

flowchart LR
  GS[GeoSync] --> QF[QFieldCloud API/CLI]
  GS --> MM[Mergin Maps CLI/DB Sync]
  GS --> AG[ArcGIS API]
  GS --> GE[GeoServer]
  GS --> SSH[SSH/rsync targets]
  GS --> QP[qgis-publish-service]
  GS --> FB[FormBuilder upstream]
  GS --> AN[Analysis upstream]
  GS --> CT[Central auth]
  QF & MM & AG --> PG[(PostGIS)]

QGIS publishing zips the synchronized directory, requires a .qgs or .qgz, POSTs database credentials and the bundle to /api/geosync-publish, and stores returned publisher ID/viewer URL. The client currently disables TLS peer/host verification; keep the upstream on trusted loopback/private networking until that is fixed.

Scaling and consistency

The architecture assumes one control-plane host with one named pipe and host-level systemd. Multiple PHP workers can share the metadata database, but FIFO writes, mutable config files, project work directories, and service control are not coordinated for multiple GeoSync hosts. Horizontal scaling is unsupported without shared storage, a distributed command queue/lock strategy, and worker placement. PostgreSQL and worker services may be scaled independently according to their own products.