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.
| 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.
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)
└── ...
- Docker Engine + Docker Compose plugin
openssl(for secret generation indeploy.sh)- Country/project rasters and metadata in
~/Downloads/FAO/AFACI/<COUNTRY>/output/(*.tif,*.map,*.xml). Adjust the path indeploy.shif your data lives elsewhere.
cd sis-dev
./deploy.shOn the first run deploy.sh will:
- Generate
.envwith random per-deployment secrets (POSTGRES_PASSWORD,POSTGRES_GLOSIS_PASSWORD,SECRET_KEY,WEB_MAPPING_API_KEY) andchmod 600it..envis gitignored — never commit it. - 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. - Bring up sis-database and apply
init.sql+ the schema dump. - Rotate the
sis_glosisPostgres role password to the value in.env. - Insert dummy data (
api.insert_dummy_data) and seedapi.setting. - Seed the web-mapping API client with the value of
WEB_MAPPING_API_KEY. - Start sis-api, then provision the
adminuser with a fresh random password and print it once at the very end of the run. - Start sis-api-glosis, sis-nginx, sis-web-services, sis-metadata,
sis-web-mapping. Copies the country's
.tif/.map/.xmlfiles into the right volumes and runspycsw-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.
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-mappingEdit 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.
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.
sis-api-glosis is shipped but disabled by default. To join the GloSIS
Federation:
- Log into the SPA as
admin. - Open Administration → GloSIS Federation.
- Click Enable. The node generates a singleton federation token (shown
on the same panel) and exposes
GET /manifestGET /profileGET /observationon host port 8006 (or via your nginx, behind/glosis/).
- 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.
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 execfor 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.auditlog enforced by SQL trigger - baseline hardening headers (
X-Frame-Options,X-Content-Type-Options,Referrer-Policy,Permissions-Policy) /docs,/redocand/openapi.jsondisabled in production (setENABLE_DOCS=truein 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.
See LICENSE.