Read a European Digital Credential: keep the file untouched, take what it states, and say plainly whether its seal was checked #670

Closed
opened 2026-10-03 01:58:49 +00:00 by tiagoagueda · 0 comments
Owner

From the list of 2026-10-02, under Documents: "Add diploma, supplement diploma, and certificates, including EDC." Assumption, to be confirmed: "EDC" here is the Europass European Digital Credential for Learning, the sealed digital certificate that universities and other issuers now award. If something else was meant, this issue is the wrong one. Nothing in Postulo reads one today, and nothing in Postulo checks a third party's signature.

A correction to the usual description. An EDC was a signed XML file until 2023. Since version 3 of the European Learning Model (May 2023) and the credential infrastructure's release 2.0 (June 2023), credentials are JSON-LD aligned with the W3C Verifiable Credential model, signed with JAdES; old XML credentials in Europass wallets were converted automatically (https://europass.europa.eu/en/news/updates-european-digital-credential-infrastructure, https://europass.europa.eu/en/information-developers). A reader written for signed XML would read the wrong format.

What exists today

  • The europass plugin reads CVs and nothing else. It is an importer shipped inside the core (src/postulo/plugins/europass/__init__.py:30-85): the PDF europass.europa.eu gives you with its attached Candidate XML, and the older SkillsPassport XML and JSON (reader.py:1-60). A JSON file with none of those names is not recognised (__init__.py:55-80) and is refused as "not a Europass file" (reader.py:163-181).
  • The importer contract turns bytes into a Record of career entries and touches no database; the import only ever adds (resume/importing.py:25-57, reader.py docstring). refuse_unreadable caps a file at 5 MiB and refuses a document type declaration in XML (plugins/base.py:336-370).
  • How a stranger's file is read here. "Nothing is fetched. No schema is resolved" (reader.py docstring), a nesting cap on JSON (MAX_DEPTH = 40, :94), no assumption that a key exists or has the type it should, and a PDF's attachment is found by hand "without a PDF library", because the alternative is "a dependency it would have to trust with a stranger's file" (plugins/europass/pdf.py:1-27).
  • Education has the fields of a degree and no level. Education is qualification, institution, place, dates and grade (resume/models.py:101-117); an EQF level survives only as the text "EQF 6" inside grade from the Europass reader (plugins/europass/candidate.py:500-520), after #235 found the reader inventing levels it had not been given.
  • No signature of anybody else's is checked. The one Postulo verifies is the plugin catalogue's: Ed25519 over the index's bytes, against a key the instance was configured with (plugins/catalogue.py:155-170), the rule being that trust is "decided by a key, never by a name" (plugins/provenance.py:16). It uses cryptography, which is a dependency.
  • A credential cannot be uploaded as a file. The extension list allows pdf, doc, docx, odt, rtf, txt, png, jpg, jpeg (documents/models.py:585-589). Certificates have a kind (documents/kinds.py:61); diplomas and supplements arrive with #669.
  • Outbound requests are inventoried. What Postulo fetches goes through the guarded client (postulo.plugins.http), with size, time and address limits, and #366 is the inventory of them. A verifier's lookups (revocation, accreditation, trust lists) would be new entries in it.
  • What the EDC Viewer checks, as Europass describes it: the format, the seal, the owner, revocation, accreditation and validity (https://europass.europa.eu/en/stakeholders/european-digital-credentials/using-european-digital-credentials-learning). Europass describes the seal as a qualified electronic seal. Revocation and accreditation need information from outside the file, and a seal needs its certificate traced to a trust anchor; how the Viewer does either was not verified here.
  • Already tracked: #244 (closed, which formats Europass writes), #318 (open, a check of the reader against a real download), #241 (plugin ideas; an EDC reader is recorded there).

Checked against main at f09b6010b.

Proposal

Three stages, each its own commit, in this order. The third is optional and is not part of the core.

  1. Keep a credential as the file it is. An uploaded file that is JSON-LD with a verifiable-credential type is accepted for the kinds certificate, diploma and diploma supplement (json joins the extension list at documents/models.py:585-589 for these kinds only), stored byte for byte (a seal covers exact bytes: nothing re-encodes, pretty-prints or "tidies" it), checksummed and delivered as an attachment under the usual file policy. documents.integrity (#663) recognises it by content, not by extension. The Files page labels it "Seal not checked by Postulo" and never valid, verified or authentic.
  2. Take what it states. A reader in the europass plugin reads an ELM v3 credential for the handful of facts a career entry needs: the title of the qualification or achievement, who awarded it, when, its EQF level and credits where they are stated, and the credential's own validity dates. Read as plain JSON: no @context is dereferenced, no JSON-LD processor is run, an embedded file is never decoded or rendered in-process, every value is escaped, and the work is bounded as the other readers are (size, depth, count). Language-tagged values are kept under their BCP 47 tag. What was read is shown beside the file as "what this credential says", read only, and a button offers "Add an education entry from this" through the existing import path (resume.importing), which only adds. The EQF level lands in the field #684 adds, and is taken only where the credential states it.
  3. Optionally, check the seal, as a separate plugin. See the decisions: not in the core.

Decisions

  • How far Postulo goes. Options: (A) keep and show the file; (B) also read it; (C) also verify the seal inside Postulo; (D) verify through a service the operator runs; (E) point the person at the official viewer. Recommended: A and B in the core, and E as a plain link ("Open it in the EDC Viewer to check it", with a sentence saying what that sends and to whom, because opening it there is the person's own act in their own browser and Postulo makes no request). C is the hard part and the risky one: a trust path from a seal certificate to the EU trusted lists, revocation and accreditation lookups, and a rule for what "valid" means that Europass can change. D keeps the code out of the core but sends the credential, which holds the person's name, to whatever service is configured.
  • Core or plugin. The reader (B) is a document reader beside the Europass CV importer and belongs in the core's own europass plugin, so its issues are filed on core. Verification (C or D) is a separate plugin in its own repository, with its own catalogues, which core never translates; it is not created here. Recorded as a comment on #241.
  • A document first, not an import. The importer contract returns a Record and touches no database, and it has nowhere to say "keep this file as the proof of that entry". Recommended: the reader is attached to the upload, so the plugin surface does not change; the import path is used only for the button that adds an entry.
  • Which format. ELM v3 JSON-LD only. The old XML (signed with an XAdES seal) is what the wallets have already converted. Recommended: refuse it with a sentence that says so (“Download it again from your Europass wallet, where it has been converted”), until someone has an old file they cannot re-download.
  • What Postulo says about a seal it did not check. Always that it did not. A grey label, not an absent one, and never a green tick. A plugin that verifies replaces the label with its own result and its own date.
  • A real sample. The reader needs credentials from a real issuer; guessing field paths from the data model is how the Europass CV reader came to believe the wrong format was current (#244). The data model's repository (https://github.com/european-commission-empl/European-Learning-Model, EUPL-1.2, archived, last pushed to on 2024-02-14) and the Europass developer page's sample credentials are the sources; their licence for being committed as test fixtures is not verified and has to be checked before one is added.
  • Personal data in a credential. Which personal fields a credential carries beyond the name is not verified. Recommended: the summary reads only the listed facts and stores no identifier from the credential.

Prior art

Every figure below was checked on 2026-10-02 (PyPI, OSV at api.osv.dev, and the project pages), except where it says not verified.

  • The standard. European Learning Model v3 and the EDC infrastructure (Issuer, Wallet, Viewer, Accreditation database): JSON-LD on the W3C Verifiable Credentials model, JAdES signature (https://europass.europa.eu/en/information-developers). The model's repository holds RDF files, JSON-LD contexts and XSD schemas, EUPL-1.2, archived (last pushed to on 2024-02-14). EUPL-1.2 lists AGPL-3.0 among its compatible licences (Appendix, "Compatible Licences"; not re-read here).
  • The EU's own code. european-digital-credentials: Java, EUPL-1.2, modules edci-issuer, edci-viewer, edci-wallet, edci-eseal-core, edci-data-model, edci-commons; archived on GitHub (last pushed to on 2024-02-02) and continued at code.europa.eu/qualifications-courses-and-credentials/european-digital-credentials (created 2024-01-11). The verifier is a module of the viewer. Not adopted: a Java application cannot be a library of a Django one.
  • DSS (https://github.com/esig/dss): the European Commission's signature framework; Java; LGPL-2.1; validates XAdES, PAdES, CAdES, JAdES and ASiC, and checks trusted lists (LOTL); has a REST validation service; version 6.5 current. This is the realistic engine for option D, run by the operator as a container. A hosted demo exists on ec.europa.eu, and sending a credential there discloses it to a third party. Not adopted in the core.
  • cryptography (Apache-2.0 or BSD-3-Clause; locked at 50.0.1, latest 50.0.2; 0 advisories affecting either): already a dependency, and what verifies the catalogue's signature. Primitives and certificate handling for a verifier; not a JAdES implementation. Used as is, by a verifier plugin.
  • PyJWT (MIT; 2.15.1 is locked and the latest; 45 advisories on record across versions, 0 affecting 2.15.1): already a dependency. It verifies a JWS given a key; support for JAdES's extra headers and detached payloads is not verified, and it has had an advisory about unknown crit extensions, which is the header JAdES leans on. Not relied on.
  • jwcrypto (LGPL-3.0-or-later; 1.6.1, released 2026-09-15; 10 advisories on record, 0 affecting 1.6.1): a fuller JOSE implementation. Compatible with AGPL-3.0 as a library; a candidate for a verifier plugin; JAdES coverage not verified. Not a dependency.
  • signxml (Apache-2.0; 5.1.0, released 2026-07-05; 4 advisories, fixed in 4.0.4; needs lxml, BSD-3-Clause, a compiled dependency with 14 advisories on record and 0 affecting 6.1.3): XML signatures, relevant only to the old XAdES credentials. Not adopted, because the old format is not read.
  • pyld (BSD-3-Clause; 3.3.0; 0 advisories): a JSON-LD processor that fetches @context documents by address. Rejected for the reader: a credential's @context would make the server fetch whatever a stranger names. The reader reads keys as plain JSON.
  • pyHanko (MIT; 0.37.0; 0 advisories): PDF signatures. Only if the Viewer's PDF export turns out to carry one; not verified.
  • Hand-written, in this repository: the Europass readers are written with the standard library for the reasons above, and catalogue.py verifies its own signature. The same reasoning covers stage 2 (JSON from a stranger, a handful of fields). It does not cover a seal check, where the standard (JAdES, eIDAS trust lists) is large and moves; the maintainer's rule to look for a library first points at DSS or a verifier plugin.

Builds on

#244 and #235 (closed), #318 (the check against real Europass files), #669 (the kinds and the link to the entry), #663 (content recognition), #684 (the EQF field), #366 (outbound lookups).

Needed by

Nothing waits on it. A verifier plugin would build on stage 1.

Done when

  • Real credentials from at least two issuers are in tests/ as fixtures (their licence checked), and tests/test_europass.py or a new tests/test_credentials.py reads each: the title, awarding body, dates, level and credits come out as stated; a file that is half right gives the half and says what was skipped; one with a hostile @context, a deeply nested value, a huge embedded attachment or a value with markup is bounded and escaped, and nothing is fetched (asserted with the network blocked).
  • tests/test_documents.py and tests/test_stores.py: a credential is stored byte for byte (its checksum is the file's), is delivered as an attachment, is copied to stores as any file is, and is labelled "Seal not checked by Postulo" on the Files page and in the API.
  • The old XML format is refused with the sentence above (tests/test_credentials.py).
  • "Add an education entry from this" adds one and overwrites nothing (tests/test_importers.py), and never takes a level the credential does not state.
  • Standing checks: the archive format goes up by one if the summary is stored (core/export.py FORMAT_VERSION, 29 on f09b6010b; one more than whatever it is when the commit lands), with a round trip; the candidate format (4) unchanged; the API (api/routers/documents.py) says whether a seal was checked; search unaffected; CV print choice not applicable; processing record (core/gdpr.py): a credential is the person's own document, and the plugin's verification lookups, if one is ever installed, are listed there as recipients; tests/test_wiki_surface.py does not apply (no new name on postulo.plugins.api); tests/test_page_coverage.py and tests/security/test_isolation_sweep.py for the summary view; the guarded-client inventory (#366) if anything fetches; strings in the working catalogues (en-GB, fr-FR, pt-PT as drafts), in the europass plugin's own catalogues, and Weblate (#349).

From the list of improvements of 2026-10-02.

From the list of 2026-10-02, under Documents: "Add diploma, supplement diploma, and certificates, including EDC." **Assumption, to be confirmed:** "EDC" here is the Europass *European Digital Credential for Learning*, the sealed digital certificate that universities and other issuers now award. If something else was meant, this issue is the wrong one. Nothing in Postulo reads one today, and nothing in Postulo checks a third party's signature. **A correction to the usual description.** An EDC was a signed XML file until 2023. Since version 3 of the European Learning Model (May 2023) and the credential infrastructure's release 2.0 (June 2023), credentials are **JSON-LD aligned with the W3C Verifiable Credential model, signed with JAdES**; old XML credentials in Europass wallets were converted automatically (<https://europass.europa.eu/en/news/updates-european-digital-credential-infrastructure>, <https://europass.europa.eu/en/information-developers>). A reader written for signed XML would read the wrong format. ## What exists today - **The `europass` plugin reads CVs and nothing else.** It is an importer shipped inside the core (`src/postulo/plugins/europass/__init__.py:30-85`): the PDF europass.europa.eu gives you with its attached Candidate XML, and the older SkillsPassport XML and JSON (`reader.py:1-60`). A JSON file with none of those names is not recognised (`__init__.py:55-80`) and is refused as "not a Europass file" (`reader.py:163-181`). - **The importer contract** turns bytes into a `Record` of career entries and touches no database; the import only ever adds (`resume/importing.py:25-57`, `reader.py` docstring). `refuse_unreadable` caps a file at 5 MiB and refuses a document type declaration in XML (`plugins/base.py:336-370`). - **How a stranger's file is read here.** "Nothing is fetched. No schema is resolved" (`reader.py` docstring), a nesting cap on JSON (`MAX_DEPTH = 40`, `:94`), no assumption that a key exists or has the type it should, and a PDF's attachment is found by hand "without a PDF library", because the alternative is "a dependency it would have to trust with a stranger's file" (`plugins/europass/pdf.py:1-27`). - **Education has the fields of a degree and no level.** `Education` is qualification, institution, place, dates and grade (`resume/models.py:101-117`); an EQF level survives only as the text "EQF 6" inside `grade` from the Europass reader (`plugins/europass/candidate.py:500-520`), after #235 found the reader inventing levels it had not been given. - **No signature of anybody else's is checked.** The one Postulo verifies is the plugin catalogue's: Ed25519 over the index's bytes, against a key the instance was configured with (`plugins/catalogue.py:155-170`), the rule being that trust is "decided by a key, never by a name" (`plugins/provenance.py:16`). It uses `cryptography`, which is a dependency. - **A credential cannot be uploaded as a file.** The extension list allows `pdf, doc, docx, odt, rtf, txt, png, jpg, jpeg` (`documents/models.py:585-589`). Certificates have a kind (`documents/kinds.py:61`); diplomas and supplements arrive with #669. - **Outbound requests are inventoried.** What Postulo fetches goes through the guarded client (`postulo.plugins.http`), with size, time and address limits, and #366 is the inventory of them. A verifier's lookups (revocation, accreditation, trust lists) would be new entries in it. - **What the EDC Viewer checks, as Europass describes it:** the format, the seal, the owner, revocation, accreditation and validity (<https://europass.europa.eu/en/stakeholders/european-digital-credentials/using-european-digital-credentials-learning>). Europass describes the seal as a qualified electronic seal. Revocation and accreditation need information from outside the file, and a seal needs its certificate traced to a trust anchor; how the Viewer does either was not verified here. - **Already tracked:** #244 (closed, which formats Europass writes), #318 (open, a check of the reader against a real download), #241 (plugin ideas; an EDC reader is recorded there). Checked against `main` at `f09b6010b`. ## Proposal Three stages, each its own commit, in this order. The third is optional and is not part of the core. 1. **Keep a credential as the file it is.** An uploaded file that is JSON-LD with a verifiable-credential `type` is accepted for the kinds certificate, diploma and diploma supplement (`json` joins the extension list at `documents/models.py:585-589` for these kinds only), stored byte for byte (a seal covers exact bytes: nothing re-encodes, pretty-prints or "tidies" it), checksummed and delivered as an attachment under the usual file policy. `documents.integrity` (#663) recognises it by content, not by extension. The Files page labels it **"Seal not checked by Postulo"** and never *valid*, *verified* or *authentic*. 2. **Take what it states.** A reader in the `europass` plugin reads an ELM v3 credential for the handful of facts a career entry needs: the title of the qualification or achievement, who awarded it, when, its EQF level and credits where they are stated, and the credential's own validity dates. Read as plain JSON: no `@context` is dereferenced, no JSON-LD processor is run, an embedded file is never decoded or rendered in-process, every value is escaped, and the work is bounded as the other readers are (size, depth, count). Language-tagged values are kept under their BCP 47 tag. What was read is shown **beside the file** as "what this credential says", read only, and a button offers "Add an education entry from this" through the existing import path (`resume.importing`), which only adds. The EQF level lands in the field #684 adds, and is taken only where the credential states it. 3. **Optionally, check the seal, as a separate plugin.** See the decisions: not in the core. ## Decisions - **How far Postulo goes.** Options: (A) keep and show the file; (B) also read it; (C) also verify the seal inside Postulo; (D) verify through a service the operator runs; (E) point the person at the official viewer. **Recommended:** A and B in the core, and E as a plain link ("Open it in the EDC Viewer to check it", with a sentence saying what that sends and to whom, because opening it there is the person's own act in their own browser and Postulo makes no request). C is the hard part and the risky one: a trust path from a seal certificate to the EU trusted lists, revocation and accreditation lookups, and a rule for what "valid" means that Europass can change. D keeps the code out of the core but sends the credential, which holds the person's name, to whatever service is configured. - **Core or plugin.** The reader (B) is a document reader beside the Europass CV importer and belongs in the core's own `europass` plugin, so its issues are filed on core. Verification (C or D) is **a separate plugin in its own repository**, with its own catalogues, which core never translates; it is not created here. Recorded as a comment on #241. - **A document first, not an import.** The importer contract returns a `Record` and touches no database, and it has nowhere to say "keep this file as the proof of that entry". **Recommended:** the reader is attached to the upload, so the plugin surface does not change; the import path is used only for the button that adds an entry. - **Which format.** ELM v3 JSON-LD only. The old XML (signed with an XAdES seal) is what the wallets have already converted. **Recommended:** refuse it with a sentence that says so (“Download it again from your Europass wallet, where it has been converted”), until someone has an old file they cannot re-download. - **What Postulo says about a seal it did not check.** Always that it did not. A grey label, not an absent one, and never a green tick. A plugin that verifies replaces the label with its own result and its own date. - **A real sample.** The reader needs credentials from a real issuer; guessing field paths from the data model is how the Europass CV reader came to believe the wrong format was current (#244). The data model's repository (<https://github.com/european-commission-empl/European-Learning-Model>, EUPL-1.2, archived, last pushed to on 2024-02-14) and the Europass developer page's sample credentials are the sources; their licence for being committed as test fixtures is not verified and has to be checked before one is added. - **Personal data in a credential.** Which personal fields a credential carries beyond the name is not verified. **Recommended:** the summary reads only the listed facts and stores no identifier from the credential. ## Prior art Every figure below was checked on 2026-10-02 (PyPI, OSV at `api.osv.dev`, and the project pages), except where it says not verified. - **The standard.** European Learning Model v3 and the EDC infrastructure (Issuer, Wallet, Viewer, Accreditation database): JSON-LD on the W3C Verifiable Credentials model, JAdES signature (<https://europass.europa.eu/en/information-developers>). The model's repository holds RDF files, JSON-LD contexts and XSD schemas, EUPL-1.2, archived (last pushed to on 2024-02-14). EUPL-1.2 lists AGPL-3.0 among its compatible licences (Appendix, "Compatible Licences"; not re-read here). - **The EU's own code.** `european-digital-credentials`: Java, EUPL-1.2, modules `edci-issuer`, `edci-viewer`, `edci-wallet`, `edci-eseal-core`, `edci-data-model`, `edci-commons`; archived on GitHub (last pushed to on 2024-02-02) and continued at `code.europa.eu/qualifications-courses-and-credentials/european-digital-credentials` (created 2024-01-11). The verifier is a module of the viewer. **Not adopted:** a Java application cannot be a library of a Django one. - **DSS** (<https://github.com/esig/dss>): the European Commission's signature framework; Java; LGPL-2.1; validates XAdES, PAdES, CAdES, JAdES and ASiC, and checks trusted lists (LOTL); has a REST validation service; version 6.5 current. This is the realistic engine for option D, run by the operator as a container. A hosted demo exists on `ec.europa.eu`, and sending a credential there discloses it to a third party. **Not adopted in the core.** - **`cryptography`** (Apache-2.0 or BSD-3-Clause; locked at 50.0.1, latest 50.0.2; 0 advisories affecting either): already a dependency, and what verifies the catalogue's signature. Primitives and certificate handling for a verifier; not a JAdES implementation. **Used as is, by a verifier plugin.** - **`PyJWT`** (MIT; 2.15.1 is locked and the latest; 45 advisories on record across versions, 0 affecting 2.15.1): already a dependency. It verifies a JWS given a key; support for JAdES's extra headers and detached payloads is not verified, and it has had an advisory about unknown `crit` extensions, which is the header JAdES leans on. **Not relied on.** - **`jwcrypto`** (LGPL-3.0-or-later; 1.6.1, released 2026-09-15; 10 advisories on record, 0 affecting 1.6.1): a fuller JOSE implementation. Compatible with AGPL-3.0 as a library; a candidate for a verifier plugin; JAdES coverage not verified. Not a dependency. - **`signxml`** (Apache-2.0; 5.1.0, released 2026-07-05; 4 advisories, fixed in 4.0.4; needs `lxml`, BSD-3-Clause, a compiled dependency with 14 advisories on record and 0 affecting 6.1.3): XML signatures, relevant only to the old XAdES credentials. Not adopted, because the old format is not read. - **`pyld`** (BSD-3-Clause; 3.3.0; 0 advisories): a JSON-LD processor that fetches `@context` documents by address. **Rejected for the reader**: a credential's `@context` would make the server fetch whatever a stranger names. The reader reads keys as plain JSON. - **`pyHanko`** (MIT; 0.37.0; 0 advisories): PDF signatures. Only if the Viewer's PDF export turns out to carry one; not verified. - **Hand-written, in this repository:** the Europass readers are written with the standard library for the reasons above, and `catalogue.py` verifies its own signature. The same reasoning covers stage 2 (JSON from a stranger, a handful of fields). It does not cover a seal check, where the standard (JAdES, eIDAS trust lists) is large and moves; the maintainer's rule to look for a library first points at DSS or a verifier plugin. ## Builds on #244 and #235 (closed), #318 (the check against real Europass files), #669 (the kinds and the link to the entry), #663 (content recognition), #684 (the EQF field), #366 (outbound lookups). ## Needed by Nothing waits on it. A verifier plugin would build on stage 1. ## Done when - Real credentials from at least two issuers are in `tests/` as fixtures (their licence checked), and `tests/test_europass.py` or a new `tests/test_credentials.py` reads each: the title, awarding body, dates, level and credits come out as stated; a file that is half right gives the half and says what was skipped; one with a hostile `@context`, a deeply nested value, a huge embedded attachment or a value with markup is bounded and escaped, and nothing is fetched (asserted with the network blocked). - `tests/test_documents.py` and `tests/test_stores.py`: a credential is stored byte for byte (its checksum is the file's), is delivered as an attachment, is copied to stores as any file is, and is labelled "Seal not checked by Postulo" on the Files page and in the API. - The old XML format is refused with the sentence above (`tests/test_credentials.py`). - "Add an education entry from this" adds one and overwrites nothing (`tests/test_importers.py`), and never takes a level the credential does not state. - Standing checks: the archive format goes up by one if the summary is stored (`core/export.py` `FORMAT_VERSION`, 29 on `f09b6010b`; one more than whatever it is when the commit lands), with a round trip; the candidate format (4) unchanged; the API (`api/routers/documents.py`) says whether a seal was checked; search unaffected; CV print choice not applicable; processing record (`core/gdpr.py`): a credential is the person's own document, and the plugin's verification lookups, if one is ever installed, are listed there as recipients; `tests/test_wiki_surface.py` does not apply (no new name on `postulo.plugins.api`); `tests/test_page_coverage.py` and `tests/security/test_isolation_sweep.py` for the summary view; the guarded-client inventory (#366) if anything fetches; strings in the working catalogues (en-GB, fr-FR, pt-PT as drafts), in the `europass` plugin's own catalogues, and Weblate (#349). From the list of improvements of 2026-10-02.
tiagoagueda added this to the 1.0.0 milestone 2026-10-03 01:58:49 +00:00
tiagoagueda modified the milestone from 1.0.0 to 0.5.0 2026-10-25 17:05:54 +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#670
No description provided.