pdfservice
RabbitMQ-RPC microservice that prepares, inserts, and validates PAdES digital signatures on PDF documents, plus checks PDFs for “active components” (JavaScript/XFA). Built on the EU DSS (eu.europa.ec.joinup.sd-dss) library and validates against the European List of Trusted Lists (LOTL/TSL). It is a Kotlin/Spring-Boot/Gradle JVM service (not Node.js) — verified build.gradle.kts:1-35, src/main/kotlin/com/facekom/pdfservice/PdfServiceApplication.kt:1-19.
Repo identity
Git remote/group is
com.facekomand GitHub orgTechTeamer(build.gradle.kts:56,88-89;README.md:64). Base branchmain; only one commit visible in this shallow source clone:7400d82 upgraded spring boot to eliminate CVA (#104).
No Git-LFS here
There is no
.gitattributesandgit lfs ls-filesis empty. The large files intl_cache/are plain-text EU trust-list XML files (e.g.https___tl_bundesnetzagentur_de_TL_DE_XML≈5.1 MB), andlibs/jars are small real jars (mq-1.4.1.jar≈105 KB). No model weights / binary blobs. Verified by listingtl_cache/andlibs/com/facekom/mq/1.4.1/.
1. Purpose & role in FaceKom
A back-end signing/validation worker invoked over RabbitMQ RPC. Four endpoints (queue names from application.properties:23-26):
| RPC queue (prod default) | Handler | Purpose |
|---|---|---|
pdf-prepareSignedDocument | PrepareSignatureEndpoint | Build PAdES placeholder + return digests to be signed externally (contentHash, dataToBeSigned). |
pdf-insertSignature | InsertSignatureEndpoint | Insert an externally-signed CMS into the PDF, returning signedPdf. |
pdf-validate | ValidateSignatureEndpoint | Validate a PDF’s signatures against EU trust lists; returns signers + validity level. |
pdf-checkActiveComponents | CheckActiveComponentsEndpoint | Detect active/dynamic content (JS, XFA, non-signature AcroForm fields). |
Signing uses the external-CMS DSS flow (PAdESWithExternalCMSService + ExternalCMSService): pdfservice never holds the private key — it prepares digests, an external signer (the FaceKom PDF Signer, see below) signs the digest, and pdfservice inserts the resulting CMS. Verified service/signature/SigningService.kt:14-32, configuration/SigningConfig.kt:64-83, endpoints in messaging/endpoints/.
External signer dependency (FaceKomPDFSigner / "pdfsigner")
README describes vendoring the private FaceKomPDFSigner (a.k.a.
pdfsigner) jar intolibs/via./gradlew publishToMavenLocal(README.md:34-53). In this checkoutlibs/contains only thecom.facekom.mqconnector jar (1.4.1) — the pdfsigner jar is not present and the onlyimplementation(files(...))line wires the mq jar (build.gradle.kts:120). The “signer” referenced insigner-config.*properties is the DSS signing config inside this repo, not a bundled pdfsigner jar.
PAdES levels: signatures are PAdES-BASELINE-LTA (CMS prepared at PAdES-BASELINE-T) — SigningConfig.kt:52-62. Validation walks levels ARCHIVAL_DATA → LONG_TERM_DATA → TIMESTAMPS → BASIC_SIGNATURES, reporting the highest that passes (model/validation/SignatureValidityLevel.kt, service/validation/PdfValidationComponent.kt:52-60).
2. Tech stack & runtime
- Language: Kotlin
2.0.20(build.gradle.kts:5). Official Kotlin code style (gradle.properties:1). - Framework: Spring Boot
3.5.14, run headless (WebApplicationType.NONE) — no HTTP server; it is a pure AMQP worker.PdfServiceApplication.kt:15,build.gradle.kts:11. - JVM: Java 17 toolchain (
build.gradle.kts:26-30); CI/Docker useopenjdk:17-jdk-slim(docker/localdev/pdfservice.Dockerfile:1,.travis.yml:4,.github/workflows/auto_release.ymlJDK 17). README local-dev workaround mentions JDK 11 only for building the legacy external pdfsigner —README.md:5. - Build tool / package manager: Gradle (Kotlin DSL) via wrapper
./gradlew; multi-project (settings.gradle). Daemon on,-Xmx2048m(gradle.properties:2-3). - PDF / crypto libs: Apache PDFBox 3.0.5, EU DSS 6.3 (many
dss-*modules), BouncyCastle 1.84, Nimbus JOSE+JWT 10.5, Jackson 2.21.1, Gson 2.10.1, RabbitMQamqp-client5.22.0 (build.gradle.kts:114-208). - In-house lib:
com.facekom:mq:1.4.1jar vendored underlibs/— providesConnectionPool,QueueManager,RpcServerOptions,QueueMessage/QueueResponse(RPC + attachment encryption/integrity). Imported throughoutmessaging/&configuration/MessagingConfig.kt. - Reproducible builds: dependency locking enabled (
build.gradle.kts:235-237); SHA-256 verification metadata written in CI (.travis.yml:23).
3. Build & run
Entrypoints
- Main class:
com.facekom.pdfservice.PdfServiceApplicationKt(build.gradle.kts:34, 248). - Generated launch script name:
pdfservice(application { applicationName = "pdfservice" },build.gradle.kts:33) → distributionbin/pdfservice. - Profile selection: reads env var
RUNTIME_ENV(default"default"); always also activates the"local"profile and an optional external configfile:/workspace/pdfservice/config/local.properties(PdfServiceApplication.kt:13-18).
No
package.json/ npm scriptsThis repo has no
package.jsonand no npm/yarn/pnpm scripts. The task’s “list each package.json scripts entry” item is N/A — build/run is entirely Gradle. The equivalent commands are the Gradle tasks below.
Key Gradle tasks (verified in build.gradle.kts)
run— Spring Bootapplicationrun (used by dev supervisorcommand=./gradlew run --info,supervisor_pdfservice_dev.conf:8).bootRun— Spring Boot run (used by compose entrypoint,docker/localdev/run-pdfservice.yml:22).test— JUnit 5 (Jupiter only; vintage excluded),per_classlifecycle, finalized by Kover XML+HTML reports (build.gradle.kts:210-229).koverXmlReport/koverHtmlReport— coverage (build.gradle.kts:220-229).bootJar— fat jar,Start-Class=...PdfServiceApplicationKt, classifierboot(build.gradle.kts:246-251).distTar/bootDistTar/distZip— gzip dist archivespdfservice-<version>.tar.gz/.zip; bundlessrc/main/resourcesintobin/, plus.versionandsupervisor_pdfservice_docker.conf(build.gradle.kts:253-280, 264-275).createVersionFile— writes.versionwithVERSION=<v>(build.gradle.kts:239-244).printVersion— prints axion-derived version (build.gradle.kts:59-63).cyclonedxBom/cyclonedxDirectBom— CycloneDX SBOM for vuln scan (build.gradle.kts:107-112; CI invulnerability_scan.yml).release— axion-release tagging (build.gradle.kts:16,20,37-54; CIauto_release.yml).- Subproject tasks:
:pdfclient:jar,:trust-list-downloader:run(used bypdfclient.Dockerfile:10and.github/scripts/check_trust_lists.py:30).
Start commands (verified)
- Dev (supervisor):
./gradlew run --infowithRUNTIME_ENV=dev(supervisor_pdfservice_dev.conf:7-8). - Docker/prod (supervisor):
/workspace/pdfservice/bin/pdfservicewithRUNTIME_ENV=docker(supervisor_pdfservice_docker.conf:7-8). - One-click localdev compose:
docker compose -f docker/localdev/run-pdfservice.yml up→ builds service, arabbitmq:3-management-alpine, and thepdfclienttest container; entrypointcd /workspace/pdfservice && chmod -R 777 ./build/resources/ && ./gradlew bootRun(run-pdfservice.yml:1-46). - Debug: inside container
supervisorctl stop allthen./gradlew run --debug-jvm(listens on 5005) —README.md:84-88.
Dockerfiles (under docker/, none at repo root)
docker/localdev/pdfservice.Dockerfile—openjdk:17-jdk-slim+fontconfig/libfreetype6,CMD ["jshell"](image is volume-mounted with source; actual run via compose entrypoint).docker/localdev/pdfclient.Dockerfile— multi-stage; builds:pdfclient:jar, creates/client/pdfclientwrapper + picocli autocomplete, runs as non-roottechteamer.- Compose files:
docker/localdev/{run,project,debug}-pdfservice.yml,docker/release-test/release-test-pdfservice.yml, plusrabbitmq.configper env.
Release flow (README.md:55-66)
Update CHANGELOG.md + version in build.gradle.kts → commit x.y.z → ./bin/tag-latest.sh x.y.z → ./gradlew distTar → upload build/distributions/pdfservice-x.y.z.tar.gz to GitHub Releases. Automated path: .github/workflows/auto_release.yml runs ./gradlew release on merge of auto/* or maintenance/* PRs into main.
4. Top-level structure
| Path | What it is |
|---|---|
src/main/kotlin/com/facekom/pdfservice/ | Main Spring Boot app (signing, validation, messaging). See §6. |
src/main/resources/ | application.properties, application-dev.properties, logback.xml, cryptography/truststore.p12, visualsignature/ (Roboto font + qtsp_sign.png). |
src/test/kotlin/com/facekom/ | Tests: pdfservice/** (unit) + automation/{functional,integration}/** (Spring/Testcontainers). See §8. |
src/test/resources/fixtures/ | Test PDFs, certs, encryption keys, JSON fixtures (pdfs/{functional,integration}/...). |
trust-list-downloader/ | Sub-Gradle-project: standalone CLI that downloads & verifies the EU LOTL and rebuilds the LOTL truststore (.p12) for the service. See §6.5. |
pdfclient/ | Sub-Gradle-project: picocli CLI to send test messages (validate/sign/check) to a running pdfservice over RabbitMQ. “TESTING PURPOSES ONLY” (pdfclient/.../App.kt:21). |
tl_cache/ | Pre-downloaded EU trust-list XML cache (~40 files) consumed offline by the service at startup. |
libs/com/facekom/mq/1.4.1/ | Vendored in-house mq connector jar (+ sources/pom/module). |
docker/ | Dockerfiles + docker-compose + rabbitmq configs for localdev and release-test. |
docs/ | AsciiDoc system docs (pdfservice.adoc + chapters/**) and PlantUML diagrams (puml/**). Largely Hungarian. |
bin/ | Two helper shell scripts (see §5). |
.github/ | CI workflows + Python automation scripts (trust-list check, vuln scan, Slack notify). |
.run/ | IntelliJ run configurations (.run.xml). |
gradle/ | Gradle wrapper + verification metadata (gradle/wrapper/). |
.scratchpad/, .travis.yml, sonar-project.properties, supervisor_*.conf | Scratch dir, Travis CI, SonarCloud, supervisord configs. |
5. Helper scripts under first-party bin/
Only two scripts in bin/ (both bash):
bin/show-coverage.sh— runs the test suite in a throwawayopenjdk:17-jdk-slimcontainer (mounting the project + the docker socket read-only so Testcontainers works), executes./gradlew clean test, thenxdg-opens the Kover HTML report via IntelliJ’s built-in server (localhost:63342/...). Verifiedbin/show-coverage.sh:1-19.bin/tag-latest.sh <tag> [--force]— release tagger. Aborts unless onmain; with--forcedeletes an existing<tag>locally+remote; deletes & re-creates thelatesttag on HEAD, creates<tag>, andgit push --tags. Verifiedbin/tag-latest.sh:1-45.
CI Python scripts (not in
bin/)
.github/scripts/holds first-party Python helpers used only by GitHub Actions:check_trust_lists.py(runs:trust-list-downloader:run, reads alert files, copies refreshed TLs totl_cache/, opens PR),vulnerability_scan.py(grype over the CycloneDX SBOM),notifications.py/notify_pr.py/notify_release.py(Slack viaSLACK_WEBHOOK_URL, suppressible withSLACK_DISABLED), andfile_handler.py(yaml/config + folder copy). Verified by reading each.
6. Key modules / services / entities
6.1 Messaging layer (messaging/)
RabbitMqConnector(messaging/RabbitMqConnector.kt) —@PostConstruct connect()registers RPC servers for the four endpoints and callsconnectionPool.connect().@DependsOn("lotlTrustListSourceProvider")so trust lists are loaded before it accepts messages;@Profile("!exclude-rabbit")lets tests disable it.prepare/insertconsume with(encryptedData, request, response, _);validate/checkActiveComponentsconsume with(_, request, _, _).- Endpoints (
messaging/endpoints/):PrepareSignatureEndpoint,InsertSignatureEndpoint,ValidateSignatureEndpoint,CheckActiveComponentsEndpoint— thin handlers delegating to services; annotated with custom logging aspects@LogBefore/@LogAfter/@LogReturnValue. Prepare/Insert callmessagingService.verifyAttachmentsAndParse(encryptedData, request)(attachment SHA-256 integrity check) before processing.
6.2 Signing (service/signature/)
SigningService— orchestratesprepareSignature(message digest + CMS-to-be-signed digest) andinsertCmsSignature(sign CMS + embed).service/signature/SigningService.kt.PadesWithExternalCmsFacade,ExternalCmsFacade,SignatureParametersProvider— wrap DSSPAdESWithExternalCMSService/ExternalCMSService.service/signature/cms/—CmsSignatureValidator(fun-interface) withCmsValidatorImpl,CryptographicCmsValidator,FormatOnlyCmsValidator: validate an externally-received CMS before inserting it.service/signature/timestamp/TimestampComponent— generates signing timestamps (RFC 3161 TSA athttps://bteszt.e-szigno.hu/tsa, creds from envFACEKOM_PDF_SIGNER_TSA_USERNAME/_PASS;application.properties:48-51,SigningConfig.kt:85-99).service/signature/visuals/— visual-signature rendering (auto text-wrap, scaling, rotation, image placement) built on DSSSignatureImageParameters. Key:VisualSignatureComponent,ContainerPlacementResolver,TextWrapper,ImageLoader,DssKeywordResolver,RotationKeywordResolver,textmeasurement/PdfBoxTextMeasurementProvider. Disabled by default in prod, enabled in dev (application.properties:53vsapplication-dev.properties:27).
6.3 Validation (service/validation/)
PdfValidationService(fun-interface) with two impls chosen byValidationConfig:TrustListPdfValidationService— validates against the EU LOTLCertificateVerifier(default / prod).LenientPdfValidationService— dev/test only, no trust lists (accepts self-signed; only used whentrustList.enabled=falseAND profile is dev/test/staging).ValidationConfig.kt:105-125.
PdfValidationComponent— core logic: first an offline crypto check (OfflinePdfValidator, internet disabled, AIA/OCSP/CRL nulled,BASIC_SIGNATURES), then walks PAdES levels online to find highest valid level; returnsSignatureValidationResponse(signers, isValid, validityLevel). Enforcespdf.versionminimum viaPdfVersionComparator(default 1.2).PdfValidationComponent.kt.LotlTrustListSourceProvider(bean namelotlTrustListSourceProvider,@Profile("!functional-test")) —@PostConstructloads/refreshes trust lists from the offline cache; throws iftrustList.cacheLocationis empty when trust lists are enabled. RunsvalidationJob.offlineRefresh().service/validation/LotlTrustListSourceProvider.kt.
6.4 Cryptography (service/cryptography/)
CryptographyService→CertificateCreationComponent(buildsP12Certificatefrom bytes),DigestComponent(SHA-256 hex, used for attachment integrity),LenientCertificateVerifierProvider.service/cryptography/verification/—SigningCertificateValidator(fun-interface) withTrustListSigningCertificateValidator(prod / trust-list),LenientSigningCertificateValidator(dev),EmptySigningCertificateValidator(dev, no check). Selected inValidationConfig.signingCertificateValidator(...).
6.5 trust-list-downloader sub-project
Standalone CLI (com.facekom.trustlistdownloader.MainKt, version 1.0.0, DSS 6.3). Main.kt flow: load application.yml → clean caches → TrustListDownloader.downloadListsAndVerify() (onlineRefresh() of the EU LOTL with pivot support + OJ signing-cert predicate) → if signature alert, abort (do not update keystore) → else rebuild combined truststore from OJ + pivots (TrustStoreBuilder) and copy the fresh .p12 into the service (chmod 777). Alert handlers (alerthandler/) write per-condition files (OJ desync, LOTL location change, signature error, project-TL outdated) that CI reads. Config trust-list-downloader/src/main/resources/application.yml pins the Official Journal URI (https://eur-lex.europa.eu/eli/C/2026/1944/oj) and LOTL URL (https://ec.europa.eu/tools/lotl/eu-lotl.xml). Verified Main.kt, service/TrustListDownloader.kt, application.yml.
6.6 pdfclient sub-project
picocli CLI (com.facekom.pdfclient.App, version 0.2) with subcommands ValidateCommand, SignCommand, CheckCommand; sends RabbitMQ messages to a running pdfservice and saves results. Bundles a test-signer jar (pdfclient/libs/com/facekom/test-signer/0.2/) and reads RABBIT_URL_OVERRIDE env or rabbit.properties. For testing only.
6.7 Cross-cutting
aspect/—LoggingAspect+@LogBefore/@LogAfter/@LogReturnValueannotations (@EnableAspectJAutoProxyinLoggerConfig); spring-boot-starter-aop.exception/—RpcException,SignatureException,SigningCertValidationException.model/— request/response DTOs per domain:signature/,validation/,visualsignature/,message/,cryptography/,checkactivecomponents/.
7. Configuration
Spring property files in src/main/resources/ (.properties, not YAML). External override via config/local.properties (PdfServiceApplication.kt:16) — README notes a teammate must supply local.json/local.properties since it holds secrets and is git-ignored (README.md:78).
application.properties(prod defaults) — verifiedapplication.properties:1-54:pdf.version=1.2trustList.enabled=true,trustList.cacheLocation=/workspace/tl_cache,trustList.useTrustStore=true,trustList.trustStore=cryptography/truststore.p12,trustList.offlineLotlSource=https___ec_europa_eu_tools_lotl_eu_lotl_xmlqueue.validateRpcName=pdf-validate,queue.prepareRpcName=pdf-prepareSignedDocument,queue.insertSignatureRpcName=pdf-insertSignature,queue.checkPdfActiveComponentsRpcName=pdf-checkActiveComponentsqueue.urls[0]=amqps://rabbitmq:5671(TLS),queue.options.allowTlsWithoutTrustStore=false, cert/key paths under/workspace/vuer_mq_cert/client/queue.rpcTimeoutMs=10000,queue.checkActiveComponentRpcTimeoutMs=400000,queue.rpcQueueMaxSize=100alerts.certificateVerifier.onOutOfRangeDate=true,...onMissingRevocationData=truesigner-config.validations.checkCertValidityOnLoad=true,...validateCertWithTrustLists=true, TSA env-var names,signer-config.reason=signature,signer-config.visual-signature.enabled=false, font path.
application-dev.properties(profiledev) — verified:1-27: plaintext AMQPamqp://guest:guest@localhost:5672, clears rabbit certs,trustList.cacheLocation=./tl_cache,signer-config.visual-signature.enabled=true.
Property binding (Spring @ConfigurationProperties / @Value)
MessagingConfig— bindsqueue.*→RabbitMqProperties; buildsQueueConfig/ConnectionPool/QueueManagerand twoRpcServerOptionsbeans (defaultRpcServerOptions,checkActiveComponentsRpcServerOptions). Quorum-queue args defaultx-queue-type=quorum,delivery-limit=30(RabbitMqProperties.kt:15-21).RabbitMqProperties— RPC names, urls list, timeouts, prefetch (3), TLS options.configuration/RabbitMqProperties.kt.ValidationConfig— large@Configuration: trust-list/cert-verifier beans (CommonCertificateVerifier, AIA/OCSP/CRL online sources, offlineFileCacheDataLoaderwithcacheExpiration=-1,TLValidationJob, EULOTLSourcewith pivot support, keystore cert sourcePKCS12), and the dev-mode toggles. Dev-only relaxations apply only when profile ∈ {dev,test,staging}:isDevelopment(env)(ValidationConfig.kt:288-290).proxyConfig()returns null (proxying explicitly disabled —:182-186).SigningConfig— digest algo (defaultSHA256), PAdES levels (LTA / T), TSAOnlineTSPSourcewithHostConnectionauth,signWithCertOutOfDateRange(dev only).configuration/SigningConfig.kt.VisualSignatureConfig/VisualSignatureProperties— bindssigner-config.visual-signature.*; font resource bean is@ConditionalOnProperty(enabled=true).configuration/VisualSignature*.kt.LoggerConfig— single SLF4JLoggerbean named bylogging.loggerName(pdfservice-logger-prod/-dev); enables AOP.JsonConfig— lenient Gson bean.- Env vars referenced:
RUNTIME_ENV(profile),FACEKOM_PDF_SIGNER_TSA_USERNAME/FACEKOM_PDF_SIGNER_TSA_PASS(TSA creds),APP_HOME&DOCKER_USER(supervisor),RABBIT_URL_OVERRIDE(pdfclient),SLACK_WEBHOOK_URL/SLACK_DISABLED(CI).
No AJV / getconfig
Configuration is Spring
@ConfigurationProperties+@Value, not Nodegetconfig/AJV JSON-schema. The Hungariandocs/chapters/application/configuration.adocdocuments these keys (and somejwt.*/extra visual-signature keys that are not present in current code — see gaps).
8. Tests
- Framework: JUnit 5 (Jupiter), Mockito + mockito-kotlin, AssertJ, Spring Boot Test, Testcontainers (RabbitMQ), WireMock standalone.
build.gradle.kts:185-207. Lifecycleper_class, vintage engine excluded (:210-215). - Location:
src/test/kotlin/com/facekom/pdfservice/**— focused unit tests (per service/component/config; e.g.CmsValidatorImplTest,PdfVersionComparatorTest, endpoint tests).automation/functional/**— Spring-context functional tests (FunctionalTest,FunctionalTestConfig, profilefunctional-testdisablesLotlTrustListSourceProvider;TestTrustSourceProvider). Covers lenient/trust-list validation, visual signatures, check-active-components.automation/integration/**— full integration incl. RabbitMQ Testcontainers (IntegrationTest,ApplicationTest,TrustListDownloadOfflineTest,SignCmsEndpointtest signer).- Fixtures under
src/test/resources/fixtures/(PDFs, certs, encryption keys, JSON).
- Run:
./gradlew test(→ Kover XML+HTML auto-generated). Containerized helper:bin/show-coverage.sh. CI:.travis.ymlruns./gradlew clean build koverXmlReport. Coverage → SonarCloud (build/reports/kover/report.xml;build.gradle.kts:86-100,sonar-project.properties). - Sub-projects also have tests:
pdfclient/src/test/.../FileServiceTest.kt,trust-list-downloader/src/test/**(incl. internet-requiringfunctional/LotlCertificateTest.kt).
Testcontainers needs the Docker socket
Integration tests spin up real RabbitMQ containers —
bin/show-coverage.shmounts/var/run/docker.sockinto the test container for this reason.
9. Dependencies on other FaceKom services / infra
Evidenced in code/config:
- RabbitMQ (required, central): the only transport. Consumes RPC queues
pdf-prepareSignedDocument,pdf-insertSignature,pdf-validate,pdf-checkActiveComponents(application.properties:23-26,RabbitMqConnector.kt). Prod connects over AMQPSamqps://rabbitmq:5671with mutual-TLS using certs at/workspace/vuer_mq_cert/client/(application.properties:28-36). Quorum queues withdelivery-limit=30(RabbitMqProperties.kt). RPC partner = the FaceKom core/orchestration that calls these queues (the external signer signs thedataToBeSigneddigest betweenprepareandinsert). - External RFC 3161 TSA:
https://bteszt.e-szigno.hu/tsa(e-Szignó test TSA) for PAdES timestamps (application.properties:51). - EU PKI / internet (outbound): AIA, OCSP, CRL endpoints queried directly for revocation during online validation (
ValidationConfig.kt:128-154); EU LOTL athttps://ec.europa.eu/tools/lotl/eu-lotl.xml(offline-cached intl_cache/). CHANGELOG 2.0.0 notes this is now direct — the old trust-list-proxy/nginx dependency was removed (CHANGELOG.md:1-14). - Shared paths (host volumes, prod):
/workspace/pdfservice(app),/workspace/tl_cache(trust-list cache),/workspace/vuer_mq_cert/client/(mq certs). Implies avuer_docker-style compose providing RabbitMQ, PostgreSQL & nginx (README.md:70-81) — but no DB/Redis usage in pdfservice’s own code (no JDBC/Redis deps inbuild.gradle.kts; the service is stateless beyond the on-disk trust-list cache).
No HTTP server / no DB
pdfservice exposes no HTTP endpoints (
WebApplicationType.NONE) and uses no database or Redis. All I/O is RabbitMQ in + outbound HTTP(S) to PKI/TSA.
10. Verified gotchas
Trust-list cache must be pre-populated (prod)
With
trustList.enabled=true, startup throwsIllegalStateExceptioniftrustList.cacheLocation(/workspace/tl_cache) is missing/empty — the service refuses to accept RabbitMQ messages until trust lists load (LotlTrustListSourceProvider.kt:33-38,RabbitMqConnector@DependsOn). Thetl_cache/dir in-repo seeds this; CI (check_trust_lists.yml) refreshes it weekly via PR.
"Lenient" / no-trust-list validation is dev-only and silently ignored in prod
trustList.enabled=false,validateCertWithTrustLists=false,checkCertValidityOnLoad=false,signWithCertOutOfDateRange=true, and theLogOnStatusAlertrelaxations only take effect when the active profile is dev/test/staging (ValidationConfig.isDevelopment,SigningConfig.kt:101-105). In prod these toggles are forced to the strict path regardless of config — confirmed by both code anddocs/.../configuration.adoc:9,13,59-61.
External-CMS signing — pdfservice does not own the key
Signing is split:
preparereturns digests, an external signer produces the CMS,insertembeds it. The bundled FaceKomPDFSigner jar (README workaround) is absent from this clone’slibs/. If you try to run the full sign flow locally you must supply that signer (or use thepdfclienttest-signer for tests).
Default truststore password is empty string
The LOTL/keystore
PKCS12sources are opened with"".toCharArray()(empty password) —ValidationConfig.kt:274,280,trust-list-downloader/.../TrustListDownloader.kt:67. Not a typo.
application-dev.propertiespoints rabbit at localhost guestDev profile uses
amqp://guest:guest@localhost:5672with TLS disabled; do not assume prod-like AMQPS in dev (application-dev.properties:11-18).
Docs are partly aspirational / bilingual
docs/chapters/application/configuration.adoclistsjwt.*(signing/encryption keys) and severalsigner-config.visual-signature.*keys (backgroundColor/originX/width/…) that map to DSS-based visual config or to themqlib’s JWE handling —jwt.*keys are not read by any Kotlin file in this repo (grep found none). Treat the AsciiDoc as a system spec, the.properties+@ConfigurationPropertiesclasses as ground truth. Attachment encryption/JWE (A256KW, per CHANGELOG 1.0.3/1.0.4) is handled inside the vendoredcom.facekom.mqjar, not in this source tree.
Unverified / gaps
- Surveyed structurally, not line-by-line:
service/signature/visuals/**and…/textmeasurement/**(readVisualSignatureComponentin full + class names of the rest; the geometry resolversContentDimensionResolver/LayoutDimensionResolver/ContainerPlacementResolverwere not each read line-by-line).service/signature/cms/**(read theCmsSignatureValidatorinterface; the three impls’ bodies surveyed by name/test only).- All
model/**DTOs (readSignatureValidityLevel; others identified by filename, excluded from coverage perbuild.gradle.kts:80packages("*model*")). - Full
aspect/**bodies,exception/**bodies, and most*Test.ktbodies (enumerated, not read individually). trust-list-downloader/alerthandler/**,alertdetector/**,configuration/**,TrustStoreBuilder,FileHandler(readMain,TrustListDownloader,application.yml; helpers surveyed by name).pdfclient/**beyondApp.kt(commands/services identified by filename)..github/scripts/*.py—check_trust_lists.pyread in full; others read by head only.docs/chapters/**anddocs/puml/**— readpdfservice.adoc,rabbitmq.adoc,configuration.adoc; remaining chapters/PUML not read.
- The vendored
com.facekom.mqjar internals (encryption/JWE,ConnectionPool/QueueManagersemantics) are a binary dependency — not decompiled per read-only/no-vendored rules. config/local.properties/local.jsonsecret values are git-ignored and absent — exact secret keys (rabbit truststore passwords, JWE key) unknown.- The exact identity of the upstream RabbitMQ caller service (which FaceKom component publishes to these RPC queues) is not named in this repo; inferred only as “FaceKom core / external signer”.
Sources
Files read (all under /Users/levander/coding/facekom-v2-clones/pdfservice/):
README.md,CHANGELOG.md,build.gradle.kts,settings.gradle,gradle.properties,.travis.yml,.dockerignore,sonar-project.properties,supervisor_pdfservice_dev.conf,supervisor_pdfservice_docker.confbin/show-coverage.sh,bin/tag-latest.shsrc/main/kotlin/com/facekom/pdfservice/PdfServiceApplication.ktsrc/main/kotlin/com/facekom/pdfservice/messaging/RabbitMqConnector.ktsrc/main/kotlin/com/facekom/pdfservice/messaging/endpoints/{Prepare,Insert,Validate}SignatureEndpoint.kt,CheckActiveComponentsEndpoint.ktsrc/main/kotlin/com/facekom/pdfservice/configuration/{MessagingConfig,RabbitMqProperties,ValidationConfig,SigningConfig,VisualSignatureConfig,VisualSignatureProperties,JsonConfig,LoggerConfig}.ktsrc/main/kotlin/com/facekom/pdfservice/service/signature/SigningService.kt,service/signature/cms/CmsSignatureValidator.kt,service/signature/visuals/VisualSignatureComponent.ktsrc/main/kotlin/com/facekom/pdfservice/service/validation/{PdfValidationService,PdfValidationComponent,TrustListPdfValidationService,OfflinePdfValidator,LotlTrustListSourceProvider}.ktsrc/main/kotlin/com/facekom/pdfservice/service/cryptography/CryptographyService.ktsrc/main/kotlin/com/facekom/pdfservice/service/checkactivecomponents/CheckActiveComponentService.ktsrc/main/kotlin/com/facekom/pdfservice/service/message/{MessagingService,AttachmentComponent}.ktsrc/main/kotlin/com/facekom/pdfservice/model/validation/SignatureValidityLevel.ktsrc/main/resources/application.properties,application-dev.propertiessrc/test/kotlin/com/facekom/automation/AutomationTest.kt(+ test tree listing)docker/localdev/pdfservice.Dockerfile,pdfclient.Dockerfile,run-pdfservice.ymltrust-list-downloader/build.gradle.kts,src/main/kotlin/com/facekom/trustlistdownloader/Main.kt,service/TrustListDownloader.kt,src/main/resources/application.ymlpdfclient/src/main/kotlin/com/facekom/pdfclient/App.ktdocs/pdfservice.adoc,docs/chapters/misc/rabbitmq.adoc,docs/chapters/application/configuration.adoc.github/workflows/{auto_release,check_trust_lists,vulnerability_scan}.yml,.github/scripts/check_trust_lists.py(+ heads ofvulnerability_scan.py,notifications.py,notify_pr.py,notify_release.py,file_handler.py).run/pdfservice dev.run.xml- Directory listings + LFS/attribute checks for
tl_cache/,libs/com/facekom/,src/main,src/test, top-level.
Sibling docs: architecture-overview · customization-architecture · vuer_oss