Build your own water tenure storymap: CRPG's engine is now open source
The software behind tenure.crpg.info, CRPG's water tenure WebGIS and storymap, is now open source (MIT). Deploy it on your own VM with Docker Compose, add your own story and layers, and protect people in the data.
tenure.crpg.info is CRPG's water tenure WebGIS and storymap for the Cimanuk basin in West Java. You can browse the map viewer, or follow the scrollytelling story at tenure.crpg.info/story. The software behind it is now open source under the MIT licence, together with a synthetic sample basin, so you can run it without anyone's real data.
A water tenure assessment makes visible who actually uses water and on what terms, including users that permits and statistics miss. A table of tenure arrangements is hard to read. The same records on a map, with a story that walks through them, are easier for communities, officials and partners to read and to argue with.
We are releasing the tooling so that other teams can build their own. It is the companion to our open-source water tenure AI skills, described in Using AI skills to conduct a water tenure analysis.
What the repository gives you
The code is at github.com/movanet/water-tenure-storymap. It has two parts.
- A web app built with Next.js and MapLibre: a scrollytelling storymap, a map viewer with layer control and basemaps, a gallery and an about page.
- A FastAPI "tenure engine" that stores tenure records and serves them back: create, read, update and delete, GeoJSON, summary statistics and CSV export. Records use the FAO water tenure typology as their vocabulary.
Two features matter for fieldwork. The first is a sensitivity rule: a record marked aggregate_only has its location fuzzed before it is shown. The second is geotagged field-photo upload, which reads the EXIF GPS position, makes thumbnails, and can add AI captions if you want them.
Everything a deployer changes lives in content files under web/content/: the site settings, map layers, stories and gallery. The repository ships with a synthetic sample basin, clearly labelled as invented, so you can try it without anyone's real data.
What is not included is CRPG's own field data, photos and stories. That material stays with the communities and the partners who commissioned the work.
Deploy in five steps
You need a VM with Docker and Docker Compose, git, and a domain name.
- Clone the repository.
- Copy
.env.exampleto.envand set your own values, including the API token that protects writes. Keep.envout of version control. - Edit the files in
web/content/to describe your site, layers, stories and gallery. The synthetic sample is a working template. - Build and start the stack with Docker Compose.
- Point your domain at the VM and put a reverse proxy in front for HTTPS. Both containers listen only on 127.0.0.1; the repository includes an example Caddy configuration in
deploy/, anddocs/INSTALL.mdwalks through the whole setup, backups and upgrades.
git clone https://github.com/movanet/water-tenure-storymap
cd water-tenure-storymap
cp .env.example .env
# edit .env (set TENURE_API_TOKEN), then edit the files in web/content/
docker compose up -d --buildAdding your own story, layers and records
Stories, layers and the gallery live in the content files, so a first version of your own storymap is mostly editing those files. Tenure records go in through the engine's API or through an importer.
If you ran your analysis with water-tenure-skills, the importer turns a water-tenure-analysis catalogue into map records. The tenure-storymap-publish skill in that pack guides the push with data protection built in: no names, and sensitive points fuzzed. The chain is evidence, catalogue, records, map, story.
Data protection and consent
A map of who uses which water can expose the people on it. The engine can fuzz a record's location when you ask it to. Everything else is your call.
- Get consent before a record goes on a public map, and write down what was agreed: whether the arrangement may be shown, and at what precision.
- Leave names out. Describe the arrangement and the type of user, not the person.
- Mark a record aggregate_only when its exact location would identify someone. The engine then publishes it at a displaced position and leaves it out of exports. Only the location is generalised: the record's title and description stay public, so write them without names. Fuzzing protects the public display; it does not replace access control on your database and backups.
- Check photos before you upload them for faces and house fronts. The upload reads GPS from EXIF, so a photo's position becomes part of the record.
- If you switch on AI captions, find out where the images are sent first.
- Replace the synthetic sample before launch, and agree with the community or partner who owns the data what may be published.
How this was made
This software was vibe-coded. The code and documentation were written by AI coding agents (Claude Code with Anthropic Claude models, operated as CRPG's agent "Mobot") under the orchestration of Mohamad Mova Al'Afghani, who set the goals, the method and the domain rules and directed and reviewed the work. Treat it as research software: review it before relying on it.
How to cite
If you use the software in a project, report or paper, please cite it. The repository also carries a CITATION.cff file.
Al'Afghani, M. M. (2026). Water Tenure Storymap (Version 1.0.0) [Computer software; vibe-coded with AI agents, orchestrated by the author]. CRPG. https://github.com/movanet/water-tenure-storymap
AI disclosure: this article was drafted by Mobot, CRPG's AI agent, under the direction of Mohamad Mova Al'Afghani, and is published under his direction.