Map pins at the street address, not the town, when an address has a street #913

Closed
opened 2026-10-15 17:19:33 +00:00 by tiagoagueda · 1 comment
Owner

When an address has a street, its pin should sit at that street address, not at the centre of its town or postcode. Moving the pin by hand for one address stays possible and wins over anything worked out.

Decision (2026-10-15)

Street-level geocoding is wanted: this overrides #700's "no online geocoder in core". A point placed by hand or from Wikidata still wins, and an address with no street keeps the offline placement. The research into services (Nominatim, Photon, Pelias, addok/BAN, hosted options) and the recommended design are in the comment of 2026-10-15 below. The "To decide" section at the end is answered there as a recommendation.

What exists today

  • jobs/placing.py::place places an address when it is saved, offline only. It uses the postcode's centroid where fetch_geonames --postcodes was run, and otherwise the town from the GeoNames city table. placed_by records which (town | postcode | manual | wikidata). The module's opening says the street "is never read here at all".
  • Moving a pin for one address already exists (#702). Every address row in templates/partials/postal_addresses.html (Your details, a contact's form, a company's form) has a status line ("On the map at …") with Place it by hand / Move it. That link goes to jobs:address_place (jobs/views.py, the view that ends in placing.place_by_hand). A point placed by hand or from Wikidata (KEPT) is not overwritten when the words change, and take_off_by_hand gives it back to the words. The "change it per entry" half of the request is therefore already built. This issue is about the default point.
  • #700 decided how precise a pin may be, and #702 built that decision: offline town and postcode only, no online geocoder in core, and street level "only by an act on one address" through an optional geocoder plugin, per press, naming the address before it is sent. docs/THREAT-MODEL.md explains why. A street address is the most sensitive thing the map touches, and on a homelab the instance's address is the person's.

What would change

Street-level placement, in a way #700 allows or by revisiting #700. The options, none decided here:

  1. A geocoder plugin kind (#700's recommendation): a Find this address button on the address row (or on the address_place page) that names the address and the service before sending one request through the guarded client (postulo.plugins.http). The point is stored as a new placed_by value (street) and kept like manual. The first plugin could target Nominatim (1 request a second, identifying User-Agent, results cached) or an operator's own Nominatim or Photon.
  2. Automatic on save, opt-in per instance (Server settings), for companies' addresses only. Companies are not personal data the way a contact's or the person's own address is. This changes #700's line and needs the threat model and the processing record (core/gdpr.py) updated.
  3. Offline street data: not realistic in core (a Nominatim planet needs about 1 TB of disk; #700 has the figures).

Whatever is chosen:

  • A point placed by hand stays first. A street-level point is replaced only by a hand placement or by take off, not by a later save.
  • The status line says which precision a pin has ("at its street", "at its postcode", "at its town"). These are new strings for the catalogues and Weblate.
  • An address with no street keeps today's offline placement.

To decide

  • Option 1, 2 or both (for example 2 for companies and 1 for contacts and the person's own addresses).
  • Which service, and whether the instance or each person chooses it.

Related: #700, #702, #703, #701, #898.

Checked against main at 56fc9d531.

When an address has a street, its pin should sit at that street address, not at the centre of its town or postcode. Moving the pin by hand for one address stays possible and wins over anything worked out. ## Decision (2026-10-15) **Street-level geocoding is wanted: this overrides #700's "no online geocoder in core".** A point placed by hand or from Wikidata still wins, and an address with no street keeps the offline placement. The research into services (Nominatim, Photon, Pelias, addok/BAN, hosted options) and the recommended design are in the comment of 2026-10-15 below. The "To decide" section at the end is answered there as a recommendation. ## What exists today - `jobs/placing.py::place` places an address when it is saved, offline only. It uses the postcode's centroid where `fetch_geonames --postcodes` was run, and otherwise the town from the GeoNames city table. `placed_by` records which (`town` | `postcode` | `manual` | `wikidata`). The module's opening says the street "is never read here at all". - **Moving a pin for one address already exists** (#702). Every address row in `templates/partials/postal_addresses.html` (Your details, a contact's form, a company's form) has a status line ("On the map at …") with *Place it by hand* / *Move it*. That link goes to `jobs:address_place` (`jobs/views.py`, the view that ends in `placing.place_by_hand`). A point placed by hand or from Wikidata (`KEPT`) is not overwritten when the words change, and `take_off_by_hand` gives it back to the words. The "change it per entry" half of the request is therefore already built. This issue is about the default point. - #700 decided how precise a pin may be, and #702 built that decision: offline town and postcode only, **no online geocoder in core**, and street level "only by an act on one address" through an optional geocoder plugin, per press, naming the address before it is sent. `docs/THREAT-MODEL.md` explains why. A street address is the most sensitive thing the map touches, and on a homelab the instance's address is the person's. ## What would change Street-level placement, in a way #700 allows or by revisiting #700. The options, none decided here: 1. **A geocoder plugin kind** (#700's recommendation): a *Find this address* button on the address row (or on the `address_place` page) that names the address and the service before sending one request through the guarded client (`postulo.plugins.http`). The point is stored as a new `placed_by` value (`street`) and kept like `manual`. The first plugin could target Nominatim (1 request a second, identifying User-Agent, results cached) or an operator's own Nominatim or Photon. 2. **Automatic on save, opt-in per instance** (Server settings), for companies' addresses only. Companies are not personal data the way a contact's or the person's own address is. This changes #700's line and needs the threat model and the processing record (`core/gdpr.py`) updated. 3. **Offline street data**: not realistic in core (a Nominatim planet needs about 1 TB of disk; #700 has the figures). Whatever is chosen: - A point placed by hand stays first. A street-level point is replaced only by a hand placement or by *take off*, not by a later save. - The status line says which precision a pin has ("at its street", "at its postcode", "at its town"). These are new strings for the catalogues and Weblate. - An address with no street keeps today's offline placement. ## To decide - Option 1, 2 or both (for example 2 for companies and 1 for contacts and the person's own addresses). - Which service, and whether the instance or each person chooses it. Related: #700, #702, #703, #701, #898. Checked against `main` at `56fc9d531`.
Author
Owner

Decision (maintainer, 2026-10-15)

#700's "no online geocoder in core" is overridden: street-level geocoding is wanted. An address with a street gets a pin at that street address, worked out by a geocoding service. The rest of #700/#702 stands: a point placed by hand (placed_by="manual") or from Wikidata wins and is never overwritten by a geocode; take off gives the point back to the words; an address with no street keeps today's offline town/postcode placement.

What follows is the research into which service and how, and a recommended design. Everything below was read on the projects' own pages on 2026-10-15 unless it says otherwise.

The options

Service Software licence Data / licence Self-host or hosted Public endpoint terms Attribution Footprint (self-hosted)
Nominatim (OSM) GPL-3.0-or-later (Python), GPLv2 other files, Apache-2.0 osm2pgsql Lua (repo) OpenStreetMap, ODbL Both: public nominatim.openstreetmap.org, or self-hosted (PostgreSQL/PostGIS) Max 1 req/s per application (all users together), identifying User-Agent or Referer (stock library agents refused), results must be cached, no client-side autocomplete, bulk geocoding "not encouraged", app must be able to switch the service off on request, and "Please do not submit personal data or other confidential material to any of our services" (usage policy) "Clearly display attribution as suitable for your medium" (© OpenStreetMap contributors, ODbL) Planet: ≥1 TB NVMe, 128 GB RAM recommended (64 GB floor for support), ~2.5 days import; 2 GB RAM absolute minimum; no published figures for a country extract (installation)
Photon (komoot) Apache-2.0 (repo) OSM (built from a Nominatim database; weekly dumps for the planet and "selected country datasets" from GraphHopper) Both: public demo photon.komoot.io or self-hosted (Java 21, embedded OpenSearch) Demo is free "within a reasonable limit", may be throttled or banned, no guarantees OSM attribution (ODbL) Planet ~95 GB disk, 64 GB RAM recommended (less with -Xmx), updates need double the disk. Has a /structured endpoint with street, housenumber, postcode, city, countrycode (API) — a good fit for PostalAddress's parts
Pelias MIT (repo) OSM + OpenAddresses + Who's on First + GeoNames (+ CSV) Self-hosted (Elasticsearch 7/8, several services: API, placeholder, PIP, libpostal, interpolation) n/a (see Geocode.Earth) Per source (OSM: ODbL; OpenAddresses: per source, mostly attribution) The project itself says "Pelias is a complex project"; the docker setup builds a Portland test instance; no planet figures on the page. Heaviest of the three to run
Geocode.Earth Hosted Pelias, by Pelias's maintainers (site) Same as Pelias Hosted, paid: 2-week trial, then from $100/month (150k req, 10 req/s); discounts for non-profit/academic/open-source Results may be stored "for as long as you need" Visible credit to Geocode Earth and OSM required (guidelines) n/a
OpenCage Proprietary service (OSM among its sources) Results may be stored permanently (pricing/FAQ) Hosted Free trial 2,500/day, 1 req/s, "testing only", not for production OSM attribution n/a
LocationIQ Proprietary service on OSM data Free tier caches for 48 h only; paid tiers as long as a customer (pricing) Hosted Free 5,000/day, 2 req/s, "limited commercial use" with a required link back Link to LocationIQ on free tier, plus OSM n/a
Géoplateforme géocodage (France; successor of api-adresse.data.gouv.fr, which was to be decommissioned end of January 2026) Engine: addok, MIT (Python + Redis) Base Adresse Nationale, BD TOPO, cadastre; site content under Etalab 2.0 (BAN) Both: https://data.geopf.fr/geocodage/search (no key for normal calls), or addok + the BAN addok file self-hosted 50 req/s per IP (cartes.gouv.fr) Etalab 2.0 asks for the source to be named Small: France only (~26 M addresses, Redis). Best-in-class for French addresses, useless elsewhere
libpostal MIT (repo) Trained model, ~1.8 GB Library (C, Python binding pypostal) n/a n/a Not a geocoder: parses and normalises addresses, says geocoding is a non-goal. Pelias uses it internally. Postulo already stores addresses in parts, so it adds nothing here
OpenAddresses — Open address points collected from governments; "most sources require only attribution", licence varies per source (site) Data only (downloads via batch.openaddresses.io) n/a Per source Usable offline only by building an index (that is what Pelias does with it). A per-country file lookup like places.py would be possible but coverage is uneven, the licence must be tracked per source, and house-number matching is a geocoder's job; not recommended for core
Portugal: INE Base Nacional de Moradas — INSPIRE Addresses dataset built from INE's Ficheiro Nacional de Alojamentos, mainland Portugal, part of the EU high-value datasets (INE INSPIRE download); CC BY per a 2021 OSM talk-pt post (not verified on INE's own page) Data only, no public geocoding API found n/a INE Same remarks as OpenAddresses; other EU states publish INSPIRE address data too, each with its own licence and format

What the research changes

  1. Nominatim's public instance asks not to be sent personal data. A company's office address is not, in general, personal data; a person's own home address and a contact's address are. So the public instance is fine as the default for company addresses, and a person's or a contact's address must not go to it. Those can be geocoded only against a geocoder the operator runs (or one whose terms they have accepted), or placed by hand as today.
  2. One request a second for the whole instance, cached. Postulo geocodes on save, one address at a time, which is far below that; the limiter must still be instance-wide (a cache-backed token, not per worker), and a re-save that changes nothing must not ask again (placed_from already gives this).
  3. Bulk is discouraged. A backfill of existing company addresses after upgrading must trickle (≤1/s, single thread, resumable) or be left to an operator's own geocoder; never a burst on migrate.
  4. Photon's /structured endpoint maps one-to-one onto PostalAddress (street, postcode, municipality, region, country) and Nominatim also accepts structured street=/city=/postalcode=/countrycodes= parameters; send parts, not one joined string.
  5. Attribution: the map already shows © OpenStreetMap contributors for the tiles (#898); a geocoded point needs the same credit plus "under the ODbL", and the service's name where its terms ask (Geocode.Earth, LocationIQ free tier, Etalab).

Recommendation

A pluggable geocoder in core, small and synchronous, behind the guarded client:

  • Interface: one function, geocode(parts) -> (lat, lon, precision) | None, with backends selected under Server settings → Plugins → Maps (beside the tile source, #898): nominatim (default), photon, addok (BAN/Géoplateforme-compatible), off. Each backend takes a base URL, so an operator points the same backend at their own Nominatim, Photon or addok. Hosted commercial services (Geocode.Earth, OpenCage, LocationIQ) stay out of core; their terms (keys, attribution, storage limits like LocationIQ's 48 h) belong in a plugin if anyone wants one.
  • Default: Nominatim public endpoint, structured query, countrycodes from the address's country, the shared USER_AGENT (Postulo (+https://source.tiagoagueda.com/postulo/postulo)) plus Nominatim's optional email parameter with the operator's address where set (search API), Accept-Language from the person's language, one result.
  • What is sent where: company addresses geocode automatically on save when the setting is on. A person's own and a contact's addresses geocode only when (a) the configured endpoint is not the public OSM one (the operator says it is theirs) and (b) the address has show on the map on; with the public endpoint those stay at postcode/town unless placed by hand. The status line names the service before the first send ("Placed with Nominatim, OpenStreetMap"), as #700 asked.
  • Storage: the point on the address row, placed_by = "street" (new Placed choice, label "its street"), placed_from as now, so an unchanged save sends nothing. Never overwrite manual or wikidata; a later save that changes the street re-geocodes a street point; take off returns it to the offline placement.
  • Failure: no result, a timeout or HTTP 429 falls back silently to today's postcode/town point and records nothing, so the next save tries again; a 403/blocked response switches the backend off and tells the operator (the policy requires being able to switch it off on request).
  • Rate and bulk: an instance-wide 1 req/s limiter for the public endpoint (configurable for self-hosted ones); a management command (or the existing task runner) for backfill that trickles and can resume.
  • Privacy and paperwork: the setting is off by default on upgrade and named for what it does ("Look up street addresses with an online service"), with the service's URL shown; docs/THREAT-MODEL.md (the map paragraph) rewritten to say what is now sent and to whom; the processing record (core/gdpr.py) gains the recipient (the configured geocoder) for company addresses, and for personal/contact addresses only under the self-hosted condition above; the data-protection page names the service. Attribution "© OpenStreetMap contributors, ODbL" (or the configured service's) beside the map.
  • Docs: a short operator page on running your own Photon (country dump, Java 21, a few GB for one country) or addok+BAN for France, since those are the realistic self-hosted options; full-planet Nominatim is out of reach for a homelab (≥1 TB, 128 GB RAM).

New strings ("its street", the setting's label and help, the status line naming the service) go through the catalogues and Weblate as usual.

## Decision (maintainer, 2026-10-15) **#700's "no online geocoder in core" is overridden: street-level geocoding is wanted.** An address with a street gets a pin at that street address, worked out by a geocoding service. The rest of #700/#702 stands: a point placed by hand (`placed_by="manual"`) or from Wikidata wins and is never overwritten by a geocode; *take off* gives the point back to the words; an address with no street keeps today's offline town/postcode placement. What follows is the research into which service and how, and a recommended design. Everything below was read on the projects' own pages on 2026-10-15 unless it says otherwise. ## The options | Service | Software licence | Data / licence | Self-host or hosted | Public endpoint terms | Attribution | Footprint (self-hosted) | | --- | --- | --- | --- | --- | --- | --- | | **Nominatim** (OSM) | GPL-3.0-or-later (Python), GPLv2 other files, Apache-2.0 osm2pgsql Lua ([repo](https://github.com/osm-search/Nominatim)) | OpenStreetMap, ODbL | Both: public `nominatim.openstreetmap.org`, or self-hosted (PostgreSQL/PostGIS) | Max **1 req/s** per application (all users together), **identifying User-Agent or Referer** (stock library agents refused), **results must be cached**, no client-side autocomplete, bulk geocoding "not encouraged", app must be able to switch the service off on request, and **"Please do not submit personal data or other confidential material to any of our services"** ([usage policy](https://operations.osmfoundation.org/policies/nominatim/)) | "Clearly display attribution as suitable for your medium" (© OpenStreetMap contributors, ODbL) | Planet: ≥1 TB NVMe, 128 GB RAM recommended (64 GB floor for support), ~2.5 days import; 2 GB RAM absolute minimum; no published figures for a country extract ([installation](https://nominatim.org/release-docs/latest/admin/Installation/)) | | **Photon** (komoot) | Apache-2.0 ([repo](https://github.com/komoot/photon)) | OSM (built from a Nominatim database; weekly dumps for the planet and "selected country datasets" from GraphHopper) | Both: public demo `photon.komoot.io` or self-hosted (Java 21, embedded OpenSearch) | Demo is free "within a reasonable limit", may be throttled or banned, no guarantees | OSM attribution (ODbL) | Planet ~95 GB disk, 64 GB RAM recommended (less with `-Xmx`), updates need double the disk. Has a `/structured` endpoint with `street`, `housenumber`, `postcode`, `city`, `countrycode` ([API](https://github.com/komoot/photon/blob/master/docs/api-v1.md)) — a good fit for `PostalAddress`'s parts | | **Pelias** | MIT ([repo](https://github.com/pelias/pelias)) | OSM + OpenAddresses + Who's on First + GeoNames (+ CSV) | Self-hosted (Elasticsearch 7/8, several services: API, placeholder, PIP, libpostal, interpolation) | n/a (see Geocode.Earth) | Per source (OSM: ODbL; OpenAddresses: per source, mostly attribution) | The project itself says "Pelias is a complex project"; the docker setup builds a Portland test instance; no planet figures on the page. Heaviest of the three to run | | **Geocode.Earth** | Hosted Pelias, by Pelias's maintainers ([site](https://geocode.earth/)) | Same as Pelias | Hosted, paid: 2-week trial, then from $100/month (150k req, 10 req/s); discounts for non-profit/academic/open-source | Results may be stored "for as long as you need" | Visible credit to Geocode Earth and OSM required ([guidelines](https://geocode.earth/guidelines/)) | n/a | | **OpenCage** | Proprietary service (OSM among its sources) | Results may be stored permanently ([pricing/FAQ](https://opencagedata.com/pricing)) | Hosted | Free **trial** 2,500/day, 1 req/s, "testing only", not for production | OSM attribution | n/a | | **LocationIQ** | Proprietary service on OSM data | Free tier caches for 48 h only; paid tiers as long as a customer ([pricing](https://locationiq.com/pricing)) | Hosted | Free 5,000/day, 2 req/s, "limited commercial use" with a required link back | Link to LocationIQ on free tier, plus OSM | n/a | | **Géoplateforme géocodage** (France; successor of `api-adresse.data.gouv.fr`, which [was to be decommissioned end of January 2026](https://adresse.data.gouv.fr/outils/api-doc/adresse)) | Engine: [addok](https://github.com/addok/addok), MIT (Python + Redis) | Base Adresse Nationale, BD TOPO, cadastre; site content under Etalab 2.0 ([BAN](https://adresse.data.gouv.fr/donnees-nationales)) | Both: `https://data.geopf.fr/geocodage/search` (no key for normal calls), or addok + the BAN addok file self-hosted | **50 req/s per IP** ([cartes.gouv.fr](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/geocodage/)) | Etalab 2.0 asks for the source to be named | Small: France only (~26 M addresses, Redis). Best-in-class for French addresses, useless elsewhere | | **libpostal** | MIT ([repo](https://github.com/openvenues/libpostal)) | Trained model, ~1.8 GB | Library (C, Python binding `pypostal`) | n/a | n/a | **Not a geocoder**: parses and normalises addresses, says geocoding is a non-goal. Pelias uses it internally. Postulo already stores addresses in parts, so it adds nothing here | | **OpenAddresses** | — | Open address points collected from governments; "most sources require only attribution", licence varies per source ([site](https://openaddresses.io/)) | Data only (downloads via batch.openaddresses.io) | n/a | Per source | Usable offline only by building an index (that is what Pelias does with it). A per-country file lookup like `places.py` would be possible but coverage is uneven, the licence must be tracked per source, and house-number matching is a geocoder's job; not recommended for core | | **Portugal: INE Base Nacional de Moradas** | — | INSPIRE Addresses dataset built from INE's Ficheiro Nacional de Alojamentos, mainland Portugal, part of the EU high-value datasets ([INE INSPIRE download](https://inspire.ine.pt/AD/atom/downloadservice_en.html)); CC BY per [a 2021 OSM talk-pt post](https://lists.openstreetmap.org/pipermail/talk-pt/2021-March/001932.html) (**not verified on INE's own page**) | Data only, no public geocoding API found | n/a | INE | Same remarks as OpenAddresses; other EU states publish INSPIRE address data too, each with its own licence and format | ## What the research changes 1. **Nominatim's public instance asks not to be sent personal data.** A company's office address is not, in general, personal data; a person's own home address and a contact's address are. So the public instance is fine as the default **for company addresses**, and a person's or a contact's address must not go to it. Those can be geocoded only against a geocoder the operator runs (or one whose terms they have accepted), or placed by hand as today. 2. **One request a second for the whole instance, cached.** Postulo geocodes on save, one address at a time, which is far below that; the limiter must still be instance-wide (a cache-backed token, not per worker), and a re-save that changes nothing must not ask again (`placed_from` already gives this). 3. **Bulk is discouraged.** A backfill of existing company addresses after upgrading must trickle (≤1/s, single thread, resumable) or be left to an operator's own geocoder; never a burst on migrate. 4. **Photon's `/structured` endpoint** maps one-to-one onto `PostalAddress` (street, postcode, municipality, region, country) and Nominatim also accepts structured `street=`/`city=`/`postalcode=`/`countrycodes=` parameters; send parts, not one joined string. 5. **Attribution**: the map already shows © OpenStreetMap contributors for the tiles (#898); a geocoded point needs the same credit plus "under the ODbL", and the service's name where its terms ask (Geocode.Earth, LocationIQ free tier, Etalab). ## Recommendation A **pluggable geocoder in core**, small and synchronous, behind the guarded client: - **Interface**: one function, `geocode(parts) -> (lat, lon, precision) | None`, with backends selected under Server settings → Plugins → Maps (beside the tile source, #898): `nominatim` (default), `photon`, `addok` (BAN/Géoplateforme-compatible), `off`. Each backend takes a **base URL**, so an operator points the same backend at their own Nominatim, Photon or addok. Hosted commercial services (Geocode.Earth, OpenCage, LocationIQ) stay out of core; their terms (keys, attribution, storage limits like LocationIQ's 48 h) belong in a plugin if anyone wants one. - **Default**: Nominatim public endpoint, structured query, `countrycodes` from the address's country, the shared `USER_AGENT` (`Postulo (+https://source.tiagoagueda.com/postulo/postulo)`) plus Nominatim's optional `email` parameter with the operator's address where set ([search API](https://nominatim.org/release-docs/latest/api/Search/)), `Accept-Language` from the person's language, one result. - **What is sent where**: company addresses geocode automatically on save when the setting is on. A person's own and a contact's addresses geocode only when (a) the configured endpoint is not the public OSM one (the operator says it is theirs) **and** (b) the address has *show on the map* on; with the public endpoint those stay at postcode/town unless placed by hand. The status line names the service before the first send ("Placed with Nominatim, OpenStreetMap"), as #700 asked. - **Storage**: the point on the address row, `placed_by = "street"` (new `Placed` choice, label "its street"), `placed_from` as now, so an unchanged save sends nothing. Never overwrite `manual` or `wikidata`; a later save that changes the street re-geocodes a `street` point; *take off* returns it to the offline placement. - **Failure**: no result, a timeout or HTTP 429 falls back silently to today's postcode/town point and records nothing, so the next save tries again; a 403/blocked response switches the backend off and tells the operator (the policy requires being able to switch it off on request). - **Rate and bulk**: an instance-wide 1 req/s limiter for the public endpoint (configurable for self-hosted ones); a management command (or the existing task runner) for backfill that trickles and can resume. - **Privacy and paperwork**: the setting is **off by default** on upgrade and named for what it does ("Look up street addresses with an online service"), with the service's URL shown; `docs/THREAT-MODEL.md` (the map paragraph) rewritten to say what is now sent and to whom; the processing record (`core/gdpr.py`) gains the recipient (the configured geocoder) for company addresses, and for personal/contact addresses only under the self-hosted condition above; the data-protection page names the service. Attribution "© OpenStreetMap contributors, ODbL" (or the configured service's) beside the map. - **Docs**: a short operator page on running your own Photon (country dump, Java 21, a few GB for one country) or addok+BAN for France, since those are the realistic self-hosted options; full-planet Nominatim is out of reach for a homelab (≥1 TB, 128 GB RAM). New strings ("its street", the setting's label and help, the status line naming the service) go through the catalogues and Weblate as usual.
tiagoagueda added this to the 0.5.0 milestone 2026-10-25 17:04:12 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#913
No description provided.