Skip to content

Library Gaps

Philip Helger edited this page Sep 28, 2026 · 19 revisions

Library Gaps

Things peppol-cli needs that the existing libraries do not currently provide. Each was verified by reading the source; nothing here is inferred from documentation or from memory.

The Where it belongs column matters: most of these are better fixed upstream than worked around in the CLI, because other consumers need them too. A gap fixed in peppol-commons helps phoss-smp and phoss-ap as well; the same code hidden inside peppol-cli helps nobody else.

# Gap Verified finding Where it belongs
1 GLN check-digit validation peppol-id-checks validates participant values through IParticipantIdentifierValidatorSPI, but only two implementations are registered: ParticipantIdentifierValidatorNorwayOrgNumber (mod-11) and ParticipantIdentifierValidatorBelgium (mod-97). No GLN/Luhn check-digit validator exists in any src/main; "GLN" appears only in Javadoc and as the constant PeppolParticipantCountryHelper.ICD_GLN = "0088". peppol-id-checks, as a new SPI implementation. The plumbing is already there, so this is small. > TODO(verify): is rejecting a bad GLN check digit correct behaviour, or must it only warn? That decides whether it is a validator or a separate advisory check.
2 Peppol Directory client peppol-directory-businesscard provides the business card data model (v1/v2/v3) and marshallers. No Directory search client class was found anywhere in peppol-commons. Ideally a new peppol-directory-client module in peppol-commons, so peppol-cli and others share it. > TODO(verify): whether phoss-directory already contains reusable client code that should be extracted rather than written afresh.
3 Phorm client library com.helger:phorm is the server WAR; there is no phorm-client module and no published client artefact. The only real caller is phoss-ap's PhormDocumentVerifier, which is coupled to phoss-ap-api and phoss-ap-basic and so cannot be reused as-is. Two copy-paste examples live in Phorm's own test tree. A small shared phorm-client module — consumed by both phoss-ap and peppol-cli — is the better outcome than a third hand-rolled HTTP client. It would also give the API a single documented client-side contract.
4 Runtime access to code list data The code list XMLs are bundled in peppol-id's JAR under external/codelists/, but no src/main class reads them — the generated enums are the entire runtime API, and each carries its own CODE_LIST_VERSION (currently "9.7"). A single JAR therefore exposes exactly one code list version. Blocks peppol codelist diff outright. Options: a runtime reader for the bundled XMLs in peppol-id, or an authoritative published versioned code list artefact to download. Needs a decision before that feature can be planned properly.
5 A single code list version constant The version is repeated as CODE_LIST_VERSION on each generated enum; there is no repo-wide constant. A CPeppolCodeLists.VERSION in peppol-id would let a consumer report one version instead of five, and would catch a generation that updated some enums but not others. Minor, cheap.
6 SML read/query operations peppol-sml-client provides the management callers (ManageServiceMetadataServiceCaller, ManageParticipantIdentifierServiceCaller, BDMSLClient) and has no DNS code in src/main (dnsjava is test scope only). Whether a non-owning client can query registrations is unclear from the source alone. > TODO(verify): if the SML management API has no read path for third parties, then peppol sml query cannot exist as specified and should become an alias for peppol dns resolve. This needs a domain answer, not more code reading.
7 Seat ID extraction from a certificate The format is verified — PeppolIdentifierHelper.REGEX_SEAT_ID = "P[A-Z]{2}[0-9]{6}" — but no helper that extracts a Seat ID from an X509Certificate was found. A helper in peppol-commons' com.helger.peppol.security package. > TODO(verify): which certificate field carries it (subject CN component?), from the Peppol PKI documentation.
8 Constants for the SML and Directory trust stores peppol-commons/src/main/resources/truststore/ contains sml-truststore.jks and directory-truststore.jks, but neither has a Java constant in PeppolTrustStores — they are referenced only from a test-scope hash tool. Every other trust store has *_CLASSPATH, *_DESCRIPTOR_* and loaded-instance constants. PeppolTrustStores, for symmetry. Needed if the CLI is to verify SML or Directory TLS certificates against the Peppol chain rather than the JDK default trust store. > TODO(verify): are those two trust stores still current, given the G2→G3 move? They sit outside the 2018/2025 directory structure.
9 No "next CA generation" constant PeppolTrustStores has Config2018 (G2, @Deprecated since 12.3.8, JKS) and Config2025 (G3, current, PKCS12). PeppolTrustedCA is wired to Config2025 only. There is no G4 or "next" constant. Not a gap today — a forward dependency. It does mean peppol cert verify --generation currently has exactly one meaningful value, and the flag should be designed so adding G4 later is not a breaking change.
10 Two diverging transport profile enums Generated EPredefinedTransportProfileIdentifier in peppol-id (from code list 9.7) and hand-maintained ESMPTransportProfile in peppol-commons (which is what the SMP code actually uses, via SMPTransportProfileRegistry). Nothing keeps them in agreement. peppol-commons. Not a missing feature but a real consistency risk: a CLI that lists one and validates against the other would produce contradictory answers. A test asserting the two agree would be cheap insurance.
11 Per-user config file discovery ph-config resolves the classpath, the working directory and whatever config.file points at. It has no notion of XDG base directories or %APPDATA%. ph-config, as a reusable helper. Every CLI in this ecosystem needs it — norma-cli, kaltblut-cli, ph-redact-cli, the en16931-*-cli tools — so solving it once upstream is clearly better than a fourth private copy. See Configuration.
12 Config value provenance peppol config show should print where each value came from. Sources carry a priority, but it is unclear how much origin information ConfiguredValue / IConfigurationSource expose. ph-config, if the information is not already reachable. > TODO(verify): read ConfiguredValue and IConfigurationSource to establish what is available before assuming this is a gap.
13 Config secret handling ph-config has no notion of a secret value, no redaction and no file-permission checking. Arguably the CLI's own concern, but a "this key is secret" marker in ph-config would let every consumer redact consistently instead of each maintaining its own key-name pattern. > TODO(decide): upstream or local?

Non-blocking findings worth fixing anyway

These came out of the research and are not peppol-cli requirements, but they will mislead whoever implements it.

  1. peppol-commons README is out of date. The usage examples around lines 270–290 use PeppolURLProvider.INSTANCE and ESML.DIGIT_TEST; neither exists in 13.0.1-SNAPSHOT. The current equivalents are PeppolNaptrURLProvider.INSTANCE and ESML.PEPPOL_TEST / ESML.PEPPOL_PRODUCTION. ESML's own class Javadoc still lists the removed DIGIT_PRODUCTION / DIGIT_TEST constants.
  2. The MD5 misconception. The participant hash is SHA-256 + Base32 with padding stripped (AbstractBDXLURLProvider); the MD5-based PeppolURLProvider was removed. Stale documentation saying MD5 is an easy way for an implementer — or an AI assistant — to get this wrong, and it is worth correcting at the source.
  3. Phorm: GET /api/get/vesids is effectively unauthenticated. In ApiGetAllVESIDs.java the token check is behind a disabled guard:
    if (false)
      if (!verifyAuthOrSetForbidden (aRequestScope, aUnifiedResponse, sLogPrefix))
        return;
    Every other API endpoint enforces X-Token. This may well be deliberate — it makes the endpoint usable as a connectivity probe, which peppol-cli would benefit from — but a dead if (false) does not say whether it is intended. Worth either removing or replacing with a comment explaining the intent. Flagging it because it is a security-relevant asymmetry, not because the CLI needs it changed.
  4. Generated sources. peppol-id-datatypes, peppol-smp-datatypes, peppol-sbdh and peppol-sml-client need mvn generate-sources before IDE use. A CONTRIBUTING note would save a new contributor an hour.

How these gaps affect planning

Gap Affected feature Consequence if not closed
1 peppol id check-value GLN identifiers get syntax-only validation; the command silently under-delivers.
2 peppol dir search Needs its own HTTP client, or the feature slips out of M2.
3 peppol validate * A hand-rolled client duplicates phoss-ap's logic, so a Phorm API change breaks two places.
4 peppol codelist diff Cannot be built as specified. Should be marked deferred rather than proposed until gap 4 is resolved.
6 peppol sml query May not be implementable; would become an alias for peppol dns resolve.
7 peppol cert seat-id Needs its own extraction code, with a real risk of reading the wrong certificate field.
8 Endpoint/TLS checks against SML and Directory Falls back to the JDK trust store, which is a weaker check than verifying against the Peppol chain.
11 Config discovery Every CLI in the ecosystem keeps reinventing it.

Clone this wiki locally