Skip to content

FAO-SID/SIS-dev

Repository files navigation

SIS — Soil Information System

A spatial data infrastructure for soils, designed to be deployed as a node of the GloSIS Federation. Each instance hosts the soil data of one country or organisation and exposes:

  • a public Web Mapping application (OpenLayers) for viewing rasters and soil profiles on a map;
  • a REST API (FastAPI) for soil profiles, observations, layers, settings and admin operations;
  • an OGC API Records / CSW metadata catalogue (pyCSW) for the rasters;
  • a WMS / WCS server (MapServer) for rasters and profile points;
  • an opt-in Federation API (sis-api-glosis) that lets the GloSIS Discovery Hub query this node for soil-profile data.

The data model is based on ISO 28258.


Components

Component Tech Host port (dev) Role
sis-nginx nginx 80, 443 Front door — TLS, routing, rate limiting, headers
sis-web-mapping OpenLayers + nginx 8001 Static single-page application (SPA): map view + admin dashboard
sis-api FastAPI 8002 JWT auth, ETL, layers, settings, users, admin
sis-metadata pyCSW 8003 OGC API Records — raster metadata catalogue
sis-web-services MapServer 8004 WMS / WCS for raster layers and soil profile points
sis-database PostgreSQL+PostGIS (internal) Single shared database (multiple schemas)
sis-api-glosis FastAPI 8006 Read-only federation endpoints (manifest/profile/observation)

In production only sis-nginx (80/443) should be reachable from the network; the other ports are dev conveniences. The single docker-compose.yml currently publishes all of them — operators wanting a hardened setup should override those ports: mappings via a docker-compose.override.yml.


Project structure

sis-dev/
├── .env                                    # Per-deployment secrets (gitignored — generated by deploy.sh)
├── .env.example                            # Template
├── deploy.sh                               # End-to-end deployment script
├── docker-compose.yml
├── LICENSE
├── README.md
│
├── sis-api/                                # FastAPI admin/data API
│   ├── Dockerfile
│   ├── main.py
│   ├── shared.py                           # Auth, DB, models — shared with sis-api-glosis
│   └── requirements.txt
│
├── sis-api-glosis/                         # Federation API (read-only)
│   ├── Dockerfile
│   ├── main.py                             # /manifest /profile /observation
│   └── requirements.txt
│
├── sis-database/                           # PostgreSQL + PostGIS
│   ├── init.sql                            # Roles + extensions
│   ├── sis-database_latest_with_codelist.sql  # Schema + codelists dump
│   └── volume/                             # Postgres data dir (gitignored)
│
├── sis-metadata/                           # pyCSW
│   ├── pycsw_default.yml                   # Template (substituted at deploy time)
│   └── volume/                             # Metadata XML records
│
├── sis-nginx/
│   └── nginx.conf                          # Front-door config
│
├── sis-web-mapping/                        # SPA (built with Parcel, served by nginx)
│   ├── Dockerfile                          # Multi-stage: builder → nginx-unprivileged
│   ├── nginx-static.conf
│   ├── package.json
│   ├── public/
│   └── src/
│
├── sis-web-services/                       # MapServer
│   └── volume/                             # *.tif and *.map files
│
└── sis-data-cube/                          # Raster preprocessing helpers (GDAL)
    └── ...

Deploying a node

Prerequisites

  • Docker Engine + Docker Compose plugin
  • openssl (for secret generation in deploy.sh)
  • Country/project rasters and metadata in ~/Downloads/FAO/AFACI/<COUNTRY>/output/ (*.tif, *.map, *.xml). Adjust the path in deploy.sh if your data lives elsewhere.

First deploy

cd sis-dev
./deploy.sh

On the first run deploy.sh will:

  1. Generate .env with random per-deployment secrets (POSTGRES_PASSWORD, POSTGRES_GLOSIS_PASSWORD, SECRET_KEY, WEB_MAPPING_API_KEY) and chmod 600 it. .env is gitignored — never commit it.
  2. Tear down any prior SIS containers (docker compose down -v) and wipe the local Postgres volume. It does not touch other containers on the host.
  3. Bring up sis-database and apply init.sql + the schema dump.
  4. Rotate the sis_glosis Postgres role password to the value in .env.
  5. Insert dummy data (api.insert_dummy_data) and seed api.setting.
  6. Seed the web-mapping API client with the value of WEB_MAPPING_API_KEY.
  7. Start sis-api, then provision the admin user with a fresh random password and print it once at the very end of the run.
  8. Start sis-api-glosis, sis-nginx, sis-web-services, sis-metadata, sis-web-mapping. Copies the country's .tif / .map / .xml files into the right volumes and runs pycsw-admin.py load-records.

When the script finishes you'll see a banner like:

============================================================
 SIS deployment complete.
------------------------------------------------------------
 Admin login:    admin
 Admin password: <random 16-char string>

 Save this password now — it will not be shown again.
 Change it via the Administration tab after first login.
============================================================

Visit http://localhost/ to see the SPA.

Subsequent deploys (same node)

Re-running deploy.sh is destructive — it wipes the database volume and re-applies the schema. Use it for rebuild-from-scratch flows. To rebuild only one or two services without touching the data, prefer:

docker compose up -d --build --no-deps sis-api sis-web-mapping

Per-country settings

Edit the COUNTRY, COUNTRY_LONG and ORG_LOGO_URL variables at the top of deploy.sh, plus the dummy-data bounding boxes if you want them tuned to the country's footprint.


Configuration: .env

Generated automatically on first ./deploy.sh. Reference template is .env.example. Keys:

Key Used by
POSTGRES_HOST/PORT/DB/USER sis-api, sis-metadata
POSTGRES_PASSWORD sis-api, sis-metadata, deploy.sh
POSTGRES_GLOSIS_USER sis-api-glosis (read-only role)
POSTGRES_GLOSIS_PASSWORD sis-api-glosis
SECRET_KEY sis-api JWT signing
ACCESS_TOKEN_EXPIRE_MINUTES sis-api
WEB_MAPPING_API_KEY sis-web-mapping (baked into bundle), seeded into api.api_client

All four secret values are unique per node. Sharing them across deployments defeats the per-node JWT and key separation.


GloSIS Federation

sis-api-glosis is shipped but disabled by default. To join the GloSIS Federation:

  1. Log into the SPA as admin.
  2. Open Administration → GloSIS Federation.
  3. Click Enable. The node generates a singleton federation token (shown on the same panel) and exposes
    • GET /manifest
    • GET /profile
    • GET /observation on host port 8006 (or via your nginx, behind /glosis/).
  4. Copy the token plus the endpoint URLs to the GloSIS Discovery Hub admin.

The data exposed is exactly the soil profiles currently visible on this node (controlled under Layers → Soil profiles). Rasters travel separately via the public metadata catalogue and the WMS endpoint.

sis-api-glosis connects to Postgres as the read-only sis_glosis role, which has SELECT only on three views (vw_api_manifest, vw_api_profile, vw_api_observation) plus the federation token view. Even with a valid token, callers cannot read or write anything else.

Click Disable to revoke access without losing the token, or Disable & Delete token to revoke and force re-issuance on next Enable.


Security notes

The deployment pattern enforces:

  • per-deployment secrets generated at install time, never committed
  • no default admin password — the password is randomized and printed once
  • Postgres is not published to the host (use docker exec for psql)
  • read-only Postgres role for federation traffic
  • JWT revocation on password change (via password_changed_at)
  • account lockout after 5 failed logins (15-minute window)
  • nginx-level rate limiting on /api/auth/login
  • append-only api.audit log enforced by SQL trigger
  • baseline hardening headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
  • /docs, /redoc and /openapi.json disabled in production (set ENABLE_DOCS=true in the env to re-enable for local dev)
  • CSV upload caps (50 MB / 200 000 rows) and table-name length safety in the ETL path
  • public metadata popup escapes every interpolation (no XSS via pyCSW records)

To add TLS, drop a cert into sis-nginx/ssl/, uncomment the HTTPS server block + the HSTS header in sis-nginx/nginx.conf, and uncomment the ./sis-nginx/ssl:/etc/nginx/ssl:ro mount in docker-compose.yml.


License

See LICENSE.

About

GloSIS-dev

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors