{"id":26522595,"url":"https://github.com/miracum/fhir-pseudonymizer","last_synced_at":"2026-03-13T14:31:51.793Z","repository":{"id":39979331,"uuid":"349577150","full_name":"miracum/fhir-pseudonymizer","owner":"miracum","description":"A REST service to pseudonymize and anonymize FHIR® resources.","archived":false,"fork":false,"pushed_at":"2026-03-04T21:53:46.000Z","size":1206,"stargazers_count":19,"open_issues_count":5,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-05T01:44:19.125Z","etag":null,"topics":["anonymization","fhir","health-informatics","healthcare","pseudonymization"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/miracum.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-03-19T23:09:10.000Z","updated_at":"2026-03-04T21:52:02.000Z","dependencies_parsed_at":"2023-10-23T12:26:17.931Z","dependency_job_id":"b1cb50e2-c5c1-44c7-9d0f-802f0067c94b","html_url":"https://github.com/miracum/fhir-pseudonymizer","commit_stats":null,"previous_names":[],"tags_count":65,"template":false,"template_full_name":null,"purl":"pkg:github/miracum/fhir-pseudonymizer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miracum%2Ffhir-pseudonymizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miracum%2Ffhir-pseudonymizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miracum%2Ffhir-pseudonymizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miracum%2Ffhir-pseudonymizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/miracum","download_url":"https://codeload.github.com/miracum/fhir-pseudonymizer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miracum%2Ffhir-pseudonymizer/sbom","scorecard":{"id":1236593,"data":{"date":"2025-08-25T19:41:27Z","repo":{"name":"github.com/miracum/fhir-pseudonymizer","commit":"9b2ac47cb8393279adb8979eecdf60fa79c64bb4"},"scorecard":{"version":"v5.2.1","commit":"ab2f6e92482462fe66246d9e32f642855a691dc1"},"score":8,"checks":[{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: RenovateBot: .renovaterc.json:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#dependency-update-tool"}},{"name":"Maintained","score":6,"reason":"8 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 6","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#maintained"}},{"name":"Code-Review","score":1,"reason":"Found 1/9 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":10,"reason":"GitHub workflow tokens follow principle of least privilege","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yaml:122","Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yaml:240","Warn: jobLevel 'security-events' permission set to 'write': .github/workflows/ci.yaml:243","Info: jobLevel 'actions' permission set to 'read': .github/workflows/ci.yaml:244","Warn: jobLevel 'security-events' permission set to 'write': .github/workflows/ci.yaml:26","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/ci.yaml:21","Warn: jobLevel 'packages' permission set to 'write': .github/workflows/ci.yaml:23","Info: jobLevel 'actions' permission set to 'read': .github/workflows/ci.yaml:25","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/release-please.yaml:15","Info: jobLevel 'contents' permission set to 'read': .github/workflows/schedule.yaml:16","Warn: jobLevel 'security-events' permission set to 'write': .github/workflows/schedule.yaml:18","Info: topLevel 'contents' permission set to 'read': .github/workflows/chaos.yaml:11","Info: topLevel 'contents' permission set to 'read': .github/workflows/ci.yaml:15","Info: topLevel 'contents' permission set to 'read': .github/workflows/lint-pr-title.yaml:11","Info: topLevel 'contents' permission set to 'read': .github/workflows/release-please.yaml:9","Info: topLevel 'contents' permission set to 'read': .github/workflows/schedule.yaml:10","Info: topLevel 'contents' permission set to 'read': .github/workflows/scorecards.yaml:20"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#security-policy"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":10,"reason":"all dependencies are pinned","details":["Info:  12 out of  12 GitHub-owned GitHubAction dependencies pinned","Info:   9 out of   9 third-party GitHubAction dependencies pinned","Info:   6 out of   6 containerImage dependencies pinned","Info:   1 out of   1 nugetCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: all commits (29) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#sast"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":10,"reason":"5 out of the last 5 releases have a total of 5 signed artifacts.","details":["Info: provenance for release artifact: ghcr.io-miracum-fhir-pseudonymizer-v2.22.10.intoto.jsonl: https://github.com/miracum/fhir-pseudonymizer/releases/tag/v2.22.10","Info: provenance for release artifact: ghcr.io-miracum-fhir-pseudonymizer-v2.22.9.intoto.jsonl: https://github.com/miracum/fhir-pseudonymizer/releases/tag/v2.22.9","Info: provenance for release artifact: ghcr.io-miracum-fhir-pseudonymizer-v2.22.8.intoto.jsonl: https://github.com/miracum/fhir-pseudonymizer/releases/tag/v2.22.8","Info: provenance for release artifact: ghcr.io-miracum-fhir-pseudonymizer-v2.22.7.intoto.jsonl: https://github.com/miracum/fhir-pseudonymizer/releases/tag/v2.22.7","Info: provenance for release artifact: ghcr.io-miracum-fhir-pseudonymizer-v2.22.6.intoto.jsonl: https://github.com/miracum/fhir-pseudonymizer/releases/tag/v2.22.6"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":8,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'master'","Info: 'force pushes' disabled on branch 'master'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'master'","Info: 'stale review dismissal' is required to merge on branch 'master'","Warn: required approving review count is 1 on branch 'master'","Warn: codeowners review is required - but no codeowners file found in repo","Info: 'last push approval' is required to merge on branch 'master'","Info: 'up-to-date branches' is required to merge on branch 'master'","Info: status check found to merge onto on branch 'master'","Info: PRs are required in order to make changes on branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#branch-protection"}},{"name":"Contributors","score":6,"reason":"project has 2 contributing companies or organizations -- score normalized to 6","details":["Info: found contributions from: mend, semantic-release"],"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#contributors"}},{"name":"CI-Tests","score":10,"reason":"29 out of 29 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#ci-tests"}}]},"last_synced_at":"2025-08-31T14:31:45.409Z","repository_id":39979331,"created_at":"2025-08-31T14:31:45.409Z","updated_at":"2025-08-31T14:31:45.409Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30468291,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-13T11:00:43.441Z","status":"ssl_error","status_checked_at":"2026-03-13T11:00:23.173Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anonymization","fhir","health-informatics","healthcare","pseudonymization"],"created_at":"2025-03-21T13:36:52.611Z","updated_at":"2026-03-13T14:31:51.782Z","avatar_url":"https://github.com/miracum.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FHIR® Pseudonymizer\n\n[![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/miracum/fhir-pseudonymizer?label=openssf%20scorecard\u0026style=flat)](https://scorecard.dev/viewer/?uri=github.com/miracum/fhir-pseudonymizer)\n[![SLSA 3](https://slsa.dev/images/gh-badge-level3.svg)](https://slsa.dev)\n\n\u003cp align=\"center\"\u003e\u003cimg width=\"100\" src=\"docs/img/logo.png\" alt=\"FHIR® Pseudonymizer Logo\"\u003e\u003c/p\u003e\n\n\u003e Send a FHIR® resource to `/fhir/$de-identify` get it back anonymized and/or pseudonymized.\n\nBased on the brilliant [Tools for Health Data Anonymization](https://github.com/microsoft/Tools-for-Health-Data-Anonymization).\n\n## Usage\n\n\u003c!-- x-release-please-start-version --\u003e\n\n```sh\ndocker run --rm -i -p 8080:8080 \\\n  -e PseudonymizationService=\"None\" \\\n  -e UseSystemTextJsonFhirSerializer=\"true\" \\\n  ghcr.io/miracum/fhir-pseudonymizer:v2.24.1\n\ncurl -X POST -H \"Content-Type:application/fhir+json\" \"http://localhost:8080/fhir/\\$de-identify\" -d @benchmark/observation.json\n```\n\n\u003c!-- x-release-please-end-version --\u003e\n\nThis uses the [default anonymization config](./src/FhirPseudonymizer/anonymization.yaml) which only changes the sample Observation's `id`.\n\nAn example for deploying using (Docker) Compose can be found in the [compose folder](./compose/README.md).\n\nThe recommended deployment is on Kubernetes. See \u003chttps://github.com/miracum/charts/tree/master/charts/fhir-pseudonymizer\u003e for a Helm Chart.\n\n### API Endpoints\n\nAn OpenAPI definition for the FHIR operation endpoints is available at `/swagger/`:\n\n![Screenshot of the OpenAPI specification](docs/img/openapi.png)\n\n#### `$de-identify`\n\nThe server provides a `/fhir/$de-identify` operation to de-identfiy received FHIR resources according to the configuration in the [anonymization.yaml](src/FhirPseudonymizer/anonymization.yaml) rules. See [Tools for Health Data Anonymization](https://github.com/microsoft/Tools-for-Health-Data-Anonymization) for more details on the anonymization rule configuration.\n\nThe service comes with a sample configuration file to help meet the requirements of HIPAA Safe Harbor Method (2)(i): [hipaa-anonymization.yaml](src/FhirPseudonymizer/hipaa-anonymization.yaml).This configuration can be used by setting `AnonymizationEngineConfigPath=/etc/hipaa-anonymization.yaml`.\n\nA new `pseudonymize` method was added to the default list of anonymization methods linked above. It uses either [gPAS](https://www.ths-greifswald.de/en/researchers-general-public/gpas/), [Vfps](https://github.com/miracum/vfps), or entici to create pseudonyms and replace the values in the resource with them.\nFor example, the following rule replaces all identifiers of type `http://terminology.hl7.org/CodeSystem/v2-0203|MR` with a pseudonym generated in the `PATIENT` domain.\n\n```yaml\nfhirPathRules:\n  - path: nodesByType('Identifier').where(type.coding.system='http://terminology.hl7.org/CodeSystem/v2-0203' and type.coding.code='MR').value\n    method: pseudonymize\n    domain: PATIENT\n```\n\nNote that if the `domain` setting is omitted, and an ID or reference is pseudonymized, then the resource name is used as the pseudonym domain. For example, pseudonymizing `\"reference\": \"Patient/123\"` will try to create a pseudonym for `123` in the `Patient` domain.\n\nWhen using [Vfps](https://github.com/miracum/vfps), the `domain` setting can instead also be set as `namespace`.\n\nNote that all methods defined in [Tools for Health Data Anonymization](https://github.com/microsoft/Tools-for-Health-Data-Anonymization) are supported.\nFor example, to clamp a patient's birthdate if they were born before January 1st 1931 to 01/01/1930, use:\n\n```yaml\nfhirPathRules:\n  - path: Patient.birthDate\n    method: generalize\n    cases:\n      \"$this \u003c @1931-01-01\": \"@1930-01-01\"\n    otherValues: keep\n```\n\n#### `$de-pseudonymize`\n\nThe `/fhir/$de-pseudonymize` operation is used to revert the `pseudonymize` and `encrypt` methods applied to any resource.\nAccessing this endpoint requires authentication. So make sure to set the `APIKEY` env var.\n\n\u003e ⚠ if decryption or de-pseudonymization of a value fails, then the original value is returned. This behavior may change or be made configurable in the future.\n\n#### `:8081/metrics`\n\nWhile not part of the \"user\" API, the application exposes metrics in the Prometheus format at the `/metrics` endpoint on port `8081`.\n\n## Configuration\n\nYou can configure the anonymization and pseudonymization rules in the `anonymization.yaml` config file.\nIt's mounted at `/etc/anonymization.yaml` within the container by default.\nSee \u003chttps://github.com/microsoft/FHIR-Tools-for-Anonymization\u003e for details on the syntax and options.\n\nAdditionally, there are some optional configuration values that can be set as environment variables:\n\n| Environment Variable                  | Description                                                                                                                                                                                                              | Default                     |\n| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- |\n| `AnonymizationEngineConfigPath`       | Path to the `anonymization.yaml` that contains the rules to transform the resources.                                                                                                                                     | `\"/etc/anonymization.yaml\"` |\n| `AnonymizationEngineConfigInline`     | The `anonymization.yaml` as an inline YAML string instead of a separate file. Takes precedence if both `Path` and `Inline` are set.                                                                                      | `\"\"`                        |\n| `ApiKey`                              | Key that must be set in the `X-Api-Key` header to allow requests to protected endpoints.                                                                                                                                 | `\"\"`                        |\n| `UseSystemTextJsonFhirSerializer`     | Enable the new `System.Text.Json`-based FHIR serializer to significantly [improve throughput and latencies](#usesystemtextjsonfhirserializer). See \u003chttps://github.com/FirelyTeam/firely-net-sdk/releases/tag/v4.0.0-r4\u003e | `false`                     |\n| `PseudonymizationService`             | The type of pseudonymization service to use. Can be one of `gPAS`, `Vfps`, `entici`, `None`                                                                                                                              | `\"gPAS\"`                    |\n| `MetricsPort`                         | The port where metrics in Prometheus format should be exposed at under the `/metrics` route.                                                                                                                             | `8081`                      |\n| `Anonymization__CryptoHashKey`        | Sets the key used by the HMAC SHA256 algorithm. This is an alternative to setting it inside the anonymization.yaml's `parameters` section and useful to more securely set sensitive information.                         | `\"\"`                        |\n| `Anonymization__EncryptKey`           | Sets the AES encryption key. This is an alternative to setting it inside the anonymization.yaml's `parameters` section and useful to more securely set sensitive information.                                            | `\"\"`                        |\n| `Anonymization__ShouldAddSecurityTag` | Whether the `Resource.meta.security` element should be filled with information about the de-identification methods applied to the resource.                                                                              | `true`                      |\n\nSee [appsettings.json](src/FhirPseudonymizer/appsettings.json) for additional options.\n\nThe application supports pseudonymization using either [gPAS](https://www.ths-greifswald.de/forscher/gpas/) or [Vfps](https://github.com/miracum/vfps) which can be configured via the `PseudonymizationService` setting.\nService-specific configuration settings are listed below.\n\n### gPAS\n\n| Environment Variable | Description                                                                                                                                                               | Default    |\n| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |\n| `gPAS__Url`          | The gPAS TTP FHIR Gateway URL. E.g. `http://localhost:8080/ttp-fhir/fhir/gpas/` for gPAS `2023.1.0`. Used if `PseudonymizationService` is set to `gPAS`.                  | `\"\"`       |\n| `gPAS__Version`      | Version of gPAS to support. There were breaking changes to the FHIR API in 1.10.2 and 1.10.3, so explicitely set this value if you are using a version newer than 1.10.1. | `\"1.10.1\"` |\n\n#### gPAS Basic Auth\n\n| Environment Variable          | Description                                     | Default |\n| ----------------------------- | ----------------------------------------------- | ------- |\n| `gPAS__Auth__Basic__Username` | The HTTP basic auth username to connect to gPAS | `\"\"`    |\n| `gPAS__Auth__Basic__Password` | The HTTP basic auth password to connect to gPAS | `\"\"`    |\n\n#### gPAS OAuth\n\n| Environment Variable               | Description                       | Default |\n| ---------------------------------- | --------------------------------- | ------- |\n| `gPAS__Auth__OAuth__TokenEndpoint` | The URL of the token endpoint     | `\"\"`    |\n| `gPAS__Auth__OAuth__ClientId`      | The client ID                     | `\"\"`    |\n| `gPAS__Auth__OAuth__ClientSecret`  | The static (shared) client secret | `\"\"`    |\n| `gPAS__Auth__OAuth__Scope`         | The scope                         | `\"\"`    |\n| `gPAS__Auth__OAuth__Resource`      | The resource                      | `\"\"`    |\n\n### Vfps\n\n| Environment Variable                            | Description                                                                                                                                                                                                                        | Default |\n| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| `Vfps__Address`                                 | The Vfps service address. Use `dns:///` scheme for client-side load-balancing.                                                                                                                                                     | `\"\"`    |\n| `Vfps__UnsafeUseInsecureChannelCallCredentials` | If set to `true`, `CallCredentials` are applied to gRPC calls made by an insecure channel. Sending authentication headers over an insecure connection has security implications and shouldn't be done in production environments.  | `true`  |\n| `Vfps__UseTls`                                  | If set to `true`, creates client-side SSL credentials loaded from disk file pointed to by the `GRPC_DEFAULT_SSL_ROOTS_FILE_PATH` environment variable. If that fails, gets the roots certificates from a well known place on disk. | `false` |\n\n#### Vfps Basic Auth\n\n| Environment Variable          | Description                                                                                                                               | Default |\n| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| `Vfps__Auth__Basic__Username` | The HTTP basic auth username to connect to the Vfps service. Used in the `Authorization: Basic` metadata header value for the gRPC calls. | `\"\"`    |\n| `Vfps__Auth__Basic__Password` | The HTTP basic auth password to connect to the Vfps service.                                                                              | `\"\"`    |\n\n### entici\n\n| Environment Variable | Description                                                                                            | Default |\n| -------------------- | ------------------------------------------------------------------------------------------------------ | ------- |\n| `entici__Url`        | The entici service base URL for FHIR operations. Used if `PseudonymizationService` is set to `entici`. | `\"\"`    |\n\nWhen using entici as a pseudonymization backend, you need to set additional settings for each rule that uses the `pseudonymize` method. These can be set under a `entici` section inside the anonymization config:\n\n```yaml\nfhirPathRules:\n  - path: nodesByType('Identifier').where(type.coding.where(system='http://terminology.hl7.org/CodeSystem/v2-0203' and code='MR').exists()).value\n    method: pseudonymize\n    # the domain will be used as the system of the identifier when invoking the entici pseudonymize operation\n    domain: https://fhir.example.com/identifiers/patient-id\n    entici:\n      # the type of FHIR resource this pseudonym should be associated with\n      resourceType: Patient\n      # (optional) the project within entici the pseudonym is a part of\n      project: some-internal-entici-project-name\n```\n\n#### entici OAuth\n\n| Environment Variable                 | Description                       | Default |\n| ------------------------------------ | --------------------------------- | ------- |\n| `entici__Auth__OAuth__TokenEndpoint` | The URL of the token endpoint     | `\"\"`    |\n| `entici__Auth__OAuth__ClientId`      | The client ID                     | `\"\"`    |\n| `entici__Auth__OAuth__ClientSecret`  | The static (shared) client secret | `\"\"`    |\n| `entici__Auth__OAuth__Scope`         | The scope                         | `\"\"`    |\n| `entici__Auth__OAuth__Resource`      | The resource                      | `\"\"`    |\n\n### Truncating Crypto-hash Length\n\nWhen using the `cryptoHash` method on a value, the result is a hex-encoded string of 64 characters length.\nYou can truncate this to a specific maximum length using the `truncateToMaxLength` setting. For example:\n\n```yaml\nfhirPathRules:\n  - path: Resource.id\n    method: cryptoHash\n    truncateToMaxLength: 32\n```\n\nWill truncate the usually 64-character-long hash to the following:\n\n```json\n{\n  \"resourceType\": \"Patient\",\n  \"id\": \"b43a73c44e6d5b57644b63d89ee90cbf\"\n}\n```\n\n## Dynamic rule settings\n\nAnonymization and pseudonymization rules in the `anonymization.yaml` config file can be overridden and/or extended on a per request basis.\n\nPseudonymization supports a `domain-prefix` rule setting which can be used to dynamically configure the target domain by providing its value as part of the request body.\n\nThe following example shows how to use this feature to use a single service configuration in order to support multiple projects which have the same basic domain names, prefixed by a project name.\n\n### Example\n\ngPAS domains for patient IDs:\n\n| project |          domain |\n| ------- | --------------: |\n| miracum | miracum-patient |\n| test    |    test-patient |\n\n`anonymization.yml`:\n\n```yml\n---\nfhirVersion: R4\nfhirPathRules:\n  - path: nodesByType('Identifier').where(type.coding.system='http://terminology.hl7.org/CodeSystem/v2-0203' and type.coding.code='MR').value\n    method: pseudonymize\n    domain: patient\n```\n\nProviding the prefix (i.e. miracum- or test-) via the request, pseudonymization can be done with the same rules for different projects.\n\n#### Request body\n\nRule settings can be provided by using the `Parameters` resource with a `settings` parameter and parts consisting of the settings key and value.\nThe `resource` parameter must contain the actual target resource.\n\nThe following request body and the (fixed) configuration settings above will result in the target domain `miracum-patient`.\n\n```json\n{\n  \"resourceType\": \"Parameters\",\n  \"parameter\": [\n    {\n      \"name\": \"settings\",\n      \"part\": [\n        {\n          \"name\": \"domain-prefix\",\n          \"valueString\": \"miracum-\"\n        }\n      ]\n    },\n    {\n      \"name\": \"resource\",\n      \"resource\": {\n        \"resourceType\": \"Bundle\",\n        \"type\": \"transaction\",\n        \"entry\": [\n          {\n            \"fullUrl\": \"urn:uuid:3bc44de3-069d-442d-829b-f3ef68cae371\",\n            \"resource\": {\n              \"resourceType\": \"Patient\",\n              \"identifier\": [\n                {\n                  \"type\": {\n                    \"coding\": [\n                      {\n                        \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\n                        \"code\": \"MR\"\n                      }\n                    ]\n                  },\n                  \"system\": \"http://acme.org/mrns\",\n                  \"value\": \"12345\"\n                }\n              ],\n              \"name\": [\n                {\n                  \"family\": \"Jameson\",\n                  \"given\": [\"J\", \"Jonah\"]\n                }\n              ],\n              \"gender\": \"male\"\n            },\n            \"request\": {\n              \"method\": \"POST\",\n              \"url\": \"Patient/12345\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}\n```\n\nNote: The domain name could also have been replaced completely by overriding the `domain` setting with the desired value. This works for all rule settings regardless of the `method` value.\n\n## Development\n\n### Start Development Fixtures (optional)\n\nTo test Vfps and tracing via Jaeger, run\n\n```sh\ndocker compose -f compose.dev.yaml up\n```\n\nto also start a Keycloak instance with pre-configured fhir-pseudonymizer client, set `--profile=keycloak`:\n\n```sh\ndocker compose -f compose.dev.yaml --profile=keycloak up\n```\n\n### Build\n\n```sh\ndotnet restore\ndotnet build\n```\n\nOr using Docker:\n\n```sh\ndocker build -t ghcr.io/miracum/fhir-pseudonymizer:local-build .\n```\n\n### Run\n\n```sh\ndotnet run --project src/FhirPseudonymizer\n```\n\n### Test\n\n```sh\ndotnet test src/FhirPseudonymizer.Tests/\n```\n\n### Install Pre-commit Hooks\n\n```sh\npre-commit install\npre-commit install --hook-type commit-msg\n```\n\n### Run iter8 SLO experiments locally\n\n```sh\nITER8_CLI_URL=\"https://github.com/iter8-tools/iter8/releases/download/v0.13.18/iter8-linux-amd64.tar.gz\"\ncurl -LSs \"${ITER8_CLI_URL}\" | tar xz\nmv linux-amd64/iter8 /usr/local/bin/iter8\nchmod +x /usr/local/bin/iter8\niter8 version\n\nkind create cluster\n\nexport IMAGE_TAG=\"iter8-test\"\n\ndocker build -t ghcr.io/miracum/fhir-pseudonymizer:${IMAGE_TAG} .\n\nkind load docker-image ghcr.io/miracum/fhir-pseudonymizer:${IMAGE_TAG}\n\nhelm upgrade --install \\\n  --set=\"image.tag=${IMAGE_TAG}\" \\\n  -f tests/iter8/values.yaml \\\n  --wait \\\n  --timeout=10m \\\n  fhir-pseudonymizer oci://ghcr.io/miracum/charts/fhir-pseudonymizer\n\nkubectl apply -f tests/iter8/experiment.yaml\n\niter8 k assert -c completed --timeout 15m\niter8 k assert -c nofailure,slos\niter8 k report\n\n# to restart:\nkubectl delete job default-1-job\nkubectl apply -f tests/iter8/experiment.yaml\n```\n\n## Benchmark\n\n\u003e **Note**\n\u003e Example runs were conducted on the following hardware:\n\n```console\nOS=Windows 11 (10.0.22000.978/21H2)\n12th Gen Intel Core i9-12900K, 1 CPU, 24 logical and 16 physical cores\n32GiB of DDR5 4800MHz RAM\nSamsung SSD 980 Pro 1TiB\n.NET SDK=7.0.101\n```\n\nPrerequisites: \u003chttps://github.com/codesenberg/bombardier\u003e\n\n```sh\ndotnet run -c Release --project=src/FhirPseudonymizer\n```\n\nIn a different terminal\n\n```sh\ncd benchmark/\n$ ./bombardier.sh\n\nBombarding http://localhost:5000/fhir/$de-identify for 1m0s using 125 connection(s)\n[====================================================================================================================] 1m0s\nDone!\nStatistics        Avg      Stdev        Max\n  Reqs/sec     13107.78    1552.49   18917.77\n  Latency        9.53ms   559.41us    53.88ms\n  Latency Distribution\n     50%     9.00ms\n     75%    11.00ms\n     90%    12.00ms\n     95%    13.00ms\n     99%    16.73ms\n  HTTP codes:\n    1xx - 0, 2xx - 786655, 3xx - 0, 4xx - 0, 5xx - 0\n    others - 0\n  Throughput:    96.37MB/s\n```\n\n### UseSystemTextJsonFhirSerializer\n\nYou can improve throughput and P99 latencies by opting-in to using the System.Text.Json based FHIR resource serializer.\nIt can be enabled via `appsettings.json` or using the `UseSystemTextJsonFhirSerializer` environment variable:\n\n```sh\nUseSystemTextJsonFhirSerializer=true dotnet run -c Release --project=src/FhirPseudonymizer\n```\n\n```console\nBombarding http://localhost:5000/fhir/$de-identify for 1m0s using 125 connection(s)\n[====================================================================================================================] 1m0s\nDone!\nStatistics        Avg      Stdev        Max\n  Reqs/sec     21508.39    3751.90   38993.50\n  Latency        5.80ms     1.63ms   442.82ms\n  Latency Distribution\n     50%     5.00ms\n     75%     6.00ms\n     90%     7.75ms\n     95%     9.00ms\n     99%    12.00ms\n  HTTP codes:\n    1xx - 0, 2xx - 1291159, 3xx - 0, 4xx - 0, 5xx - 0\n    others - 0\n  Throughput:   158.17MB/s\n```\n\n## Image signature and provenance verification\n\nPrerequisites:\n\n- [cosign](https://github.com/sigstore/cosign/releases)\n- [slsa-verifier](https://github.com/slsa-framework/slsa-verifier/releases)\n- [crane](https://github.com/google/go-containerregistry/releases)\n\nAll released container images are signed using [cosign](https://github.com/sigstore/cosign) and SLSA Level 3 provenance is available for verification.\n\n\u003c!-- x-release-please-start-version --\u003e\n\n```sh\nIMAGE=ghcr.io/miracum/fhir-pseudonymizer:v2.24.1\nDIGEST=$(crane digest \"${IMAGE}\")\nIMAGE_DIGEST_PINNED=\"ghcr.io/miracum/fhir-pseudonymizer@${DIGEST}\"\nIMAGE_TAG=\"${IMAGE#*:}\"\n\ncosign verify \\\n   --certificate-oidc-issuer=https://token.actions.githubusercontent.com \\\n   --certificate-identity-regexp=\"https://github.com/miracum/.github/.github/workflows/standard-build.yaml@.*\" \\\n   --certificate-github-workflow-name=\"ci\" \\\n   --certificate-github-workflow-repository=\"miracum/fhir-pseudonymizer\" \\\n   --certificate-github-workflow-trigger=\"release\" \\\n   --certificate-github-workflow-ref=\"refs/tags/${IMAGE_TAG}\" \\\n   \"${IMAGE_DIGEST_PINNED}\"\n\nslsa-verifier verify-image \\\n    --source-uri github.com/miracum/fhir-pseudonymizer \\\n    --source-tag ${IMAGE_TAG} \\\n    \"${IMAGE_DIGEST_PINNED}\"\n```\n\n\u003c!-- x-release-please-end-version --\u003e\n\n## Semantic versioning exclusion policies\n\nThe project's versioning follows the [SemVer](https://semver.org/) convention.\nHowever, we exclude metrics (ie. anything under the `:8081/metrics` endpoint), traces, and the contents of the container image from this.\nAlways be prepared to double-check the release notes before updating.\n\n## Attribution\n\nIcons made by [Freepik](https://www.freepik.com) from [Flaticon](https://www.flaticon.com/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiracum%2Ffhir-pseudonymizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmiracum%2Ffhir-pseudonymizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiracum%2Ffhir-pseudonymizer/lists"}