{"id":51851570,"url":"https://github.com/ictu/gbo-demo","last_synced_at":"2026-07-23T20:00:53.608Z","repository":{"id":372441254,"uuid":"1299321753","full_name":"ICTU/GBO-demo","owner":"ICTU","description":"Demo omgeving voor GBO","archived":false,"fork":false,"pushed_at":"2026-07-21T05:46:45.000Z","size":472,"stargazers_count":1,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-21T07:20:49.361Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"eupl-1.2","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ICTU.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":"publiccode.yml","codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-13T13:26:39.000Z","updated_at":"2026-07-21T05:44:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ICTU/GBO-demo","commit_stats":null,"previous_names":["ictu/gbo-demo"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ICTU/GBO-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICTU%2FGBO-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICTU%2FGBO-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICTU%2FGBO-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICTU%2FGBO-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ICTU","download_url":"https://codeload.github.com/ICTU/GBO-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICTU%2FGBO-demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35814993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-23T02:00:06.683Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-07-23T20:00:45.002Z","updated_at":"2026-07-23T20:00:53.589Z","avatar_url":"https://github.com/ICTU.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GBO — Gemeenschappelijke Bronontsluiting (referentie-demo)\n\nA live, runnable reference architecture for **GBO** (Gemeenschappelijke Bronontsluiting). It shows how a consumer (data requester) can obtain data from a source-holder over a trusted transport channel, where every request is authorized against machine-readable policy, and where the source retains control over identifier resolution.\n\nTwo access flows sit side-by-side on the same authorization pipeline:\n\n- **Consent flow** — a citizen grants a consumer permission to query a specific scope of source data. The consumer uses a consent-id to trigger the query; the source resolves it against the citizen's real identifier (BSN) inside its own trust boundary.\n- **Wallet flow** — a citizen holds an EUDI-wallet credential and discloses it to a consumer, who then requests source data using the disclosed identifier. Same policy engine, same transport, different front door.\n\nBoth flows share one authorization pipeline: FSC-Inway (transport) → PDP (context handler) → OPA (policy engine) → source-side sidecar (identifier substitution) → source.\n\n## Prerequisites\n\n- **Docker** with Compose plugin (Docker Desktop 4.x or Docker Engine + `docker compose`).\n- **~8 GB RAM** allocated to Docker (Preferences → Resources), **~10 GB disk** for images.\n\nEvery mode also needs two Postgres passwords set via env-files (compose fails loud if unset — any string works for the local demo network):\n\n- **`EUDI_POSTGRES_PASSWORD`** in `.env` — password for `postgres-eudi` (issuance-server + migrations).\n- **`FSC_POSTGRES_PASSWORD`** in `fsc-infra/.env` — shared password for all FSC-infra database users (three orgs × controller/manager/txlog + directory).\n\nThat covers the default (`make demo`, DvTP-only). For the wallet flow (`make demo-eudi` / `make demo-full`) you also need:\n\n- **nl-wallet sources** — pinned as a git submodule at `vendor/nl-wallet` (**v0.4.1, required**: the preprod wallet app rejects v0.5.0's scheme-prefixed `client_id`). Init once with `git submodule update --init vendor/nl-wallet`. Used to build the issuance-server binary from source. Override with `NLWALLET_PATH` in `.env` if you need another checkout.\n- **Three public HTTPS URLs** — `EUDI_PUBLIC_URL` (wallet reaches issuance-server), `EUDI_READER_ORIGIN_URL` (published as `requestOriginBaseUrl` in `reader_auth.json`; usually the same host as `EUDI_PUBLIC_URL`), and `EUDI_BRI_URL` (issuance-server reaches eudi-adapter). See [EUDI public reachability](#eudi-public-reachability) for the three supported options (own domain / bundled Cloudflare tunnel / ad-hoc tunnel).\n- **Six EUDI crypto slots** in `.env` — `EUDI_READER_KEY/CERT`, `EUDI_ISSUER_KEY/CERT`, `EUDI_STATUS_KEY/CERT`. `make eudi-config` (auto-run by `make demo-eudi`) renders `services/eudi-issuance-server/config/{issuance_server.toml,reader_auth.json,...}` from their `.example` templates via `envsubst`. The `.example` files contain public trust-anchors and URL placeholders; **the 6 private keys/certs are not in the public repo** — request them out-of-band from the maintainer for a working wallet-QR flow. Requires `envsubst` (`brew install gettext` on macOS).\n\nCopy the templates and fill them in:\n\n```bash\ncp .env.example .env\ncp fsc-infra/.env.example fsc-infra/.env\n# then edit both\n```\n\n## Quick Start\n\n```bash\ncd 05-demo\nmake demo             # DvTP (consent) flow only — no wallet, no public URLs needed\n```\n\n### Other modes\n\n```bash\nmake demo-minimal     # Base only (~30s, ~13 services)\n                      # Curl directly at pdp-service /evaluation for policy tests.\n\nmake demo-eudi        # Wallet flow only (~5-10 min first boot; PKI + FSC-infra + contract seed)\n                      # Requires the vendor/nl-wallet submodule + two public HTTPS URLs\n                      # — see \"EUDI public reachability\" below.\n\nmake demo-full        # Both flows on\n\nmake demo-down        # Bring everything down\n```\n\nThree front-ends run in parallel (in default/full mode):\n\n- **Consumer mock** (`http://localhost:9001`) — a stand-in for a data-consuming party (e.g. a mortgage lender). Talks to `dienstverlener-backend`.\n- **Consent portal** (`http://localhost:9002`) — a citizen-facing UI to grant and revoke consent. Talks to `consent-portal-backend`.\n- **Developer portal** (`http://localhost:9003`) — architect inspection UI: live trace view + policy inspection + FSC txlog per hop.\n\nThe developer portal also runs in `demo-minimal` and `demo-eudi` — flow tabs stay empty until the matching backend services are up.\n\n## Demo Walkthrough (Consent Flow)\n\n1. Open the **consent portal** (`:9002`) and log in as a citizen (mock DigiD, BSN from `graphql-server/mockdata/citizens.json`).\n2. Grant consent for a scope (e.g. `bd:ib:2025`) to a consumer.\n3. Open the **consumer mock** (`:9001`), enter the consent-id, click **\"Run query\"** — the consumer queries income data via HV-Outway → BD-Inway → AuthZen call to PDP → OPA → source sidecar (PI→BSN) → GraphQL.\n4. Open the **developer portal** (`:9003`) → Use tab → click \"Watch\" → the live arch strip lights up hop by hop.\n5. Revoke consent from the portal and repeat the query — OPA denies with `CONSENT_WITHDRAWN`.\n\n## \"Break things\" Guide\n\n### Edit OPA policies (Rego hot-reload)\n\nOPA watches the `policies/` directory. Edit any Rego file and save — OPA reloads automatically.\n\n```bash\n# Example: force OPA to deny everything\necho 'package dvtp.authz\nimport rego.v1\ndefault allow := false\ndefault reason := \"policy_override\"' \u003e policies/dvtp/authz.rego\n\n# Run a query — the deny surfaces in the consumer UI and the developer portal.\n\n# Restore the original policy\ngit checkout policies/dvtp/authz.rego\n```\n\n### Revoke consent\n\nClick \"Revoke consent\" in the consent portal (`:9002`), repeat the query. OPA reads the consent register, sees status=REVOKED → DENY.\n\n### View OPA decision logs\n\n```bash\ndocker compose logs -f opa\n```\n\n## Service ports\n\n| Service | Port | Description | Real/Mock |\n|---------|------|-------------|-----------|\n| Consumer mock | 9001 | Consumer UI (React/Vite) | Demo frontend |\n| Consent portal | 9002 | Citizen UI (React/Vite) | Demo frontend |\n| Developer portal | 9003 | Architect inspection (React/Vite) | Demo frontend |\n| dev-portal-backend | 9407 | Trace hub + explain endpoint | Real (Go) |\n| GraphQL Server | 9400 | Sample source with income data | Real (Go) |\n| pdp-service | 9408 | AuthZen endpoint behind FSC-Inway (P3 context handler) | Real (Go) |\n| bron-sidecar | 9411 | Source-side gateway; PI→BSN via BSNk (subject_id_type-driven) | Real (Go) |\n| additional-claims-service | 9412 | Provider policy that enriches OpenFSC access tokens | Demo configuration (Go) |\n| Consent Register | 9402 | Consent store (PIP) | Mock (Go, in-memory) |\n| BSNk Mock | 9403 | Pseudonymization service | Mock (Go, deterministic) |\n| HV-Manager UI | 8096 | Consumer-org FSC-Controller (mortgage-lender demo org) | Real (OpenFSC v2.4.0) |\n| EDI-Manager UI | 8094 | Consumer-org FSC-Controller (EUDI issuer) | Real (OpenFSC v2.4.0) |\n| BD-Manager UI | 8092 | Provider-org FSC-Controller (source-holder demo org) | Real (OpenFSC v2.4.0) |\n| OPA | 9181 | Policy Decision Point (Rego) | Real |\n| Jaeger | 9686 | Distributed tracing UI | Real |\n| OTel Collector | 9317 | Trace collection | Real |\n\n## What is real vs. demo scaffolding\n\n| Component | Status | Notes |\n|-----------|--------|-------|\n| OPA / Rego policies | **Real** | Production OPA container with real Rego evaluation |\n| OpenTelemetry + Jaeger | **Real** | Production-grade distributed tracing |\n| GraphQL Server | **Real** | Real Go GraphQL implementation |\n| FSC (Manager/Inway/Outway/Controller/txlog) | **Real** | OpenFSC v2.4.0 upstream containers, three orgs (consumer, EUDI-issuer, provider) each with their own PostgreSQL + certs |\n| pdp-service | **Real** | AuthZen endpoint behind FSC-Inway; the only policy endpoint for both flows |\n| bron-sidecar | **Real** | Source-side gateway; PI→BSN driven by the signed `subject_id_type` additional claim |\n| additional-claims-service | **Demo** | GitOps-style provider policy; production should resolve claims from the authoritative onboarding or authorization source |\n| Consent Register | **Mock** | In-memory; production would be a persistent store |\n| BSNk Mock | **Mock** | Deterministic SHA-256; real BSNk uses ElGamal on elliptic curves |\n\n## Architecture\n\nThe five-factor authorization model demonstrated:\n\n| # | Factor | Implementation in demo |\n|---|--------|------------------------|\n| ① | Org identity (mTLS) | FSC-Manager validates peer-certs; FSC-Inway includes peer_cert_chain in the AuthZen context |\n| ② | Org permission (JWT) | Provider FSC-Manager validates the grant and signs `add.{flow, subject_id_type}` returned by its Additional Claims API |\n| ③ | Access basis (consent) | pdp-service fetches consent via `GET /consents?pi=\u003cpi\u003e\u0026scope=...` on consent-register |\n| ④ | Data scope (GraphQL) | OPA checks requested fields against the dienstencatalogus (rules DVT0001/EUD0001) |\n| ⑤ | Request validity | OPA validates `pip.consent` + `resource.pi` binding + expiry |\n\n## Makefile targets\n\n```bash\nmake up      # Build and start all services\nmake down    # Stop all services\nmake logs    # Tail all service logs\nmake clean   # Stop, remove volumes and images\nmake certs   # Generate self-signed TLS certificates\n```\n\n### FSC-infra targets\n\nReal OpenFSC transport with self-hosted root-CA + directory-peer. Fully standalone from the main stack.\n\n```bash\nmake fsc-ca    # Generate root-CA + intermediate-CA in fsc-infra/pki/ca/ (idempotent)\nmake fsc-up    # Start cfssl + certportal (implies make fsc-ca)\nmake fsc-test  # Verify: test-CSR → certportal → chain-check\nmake fsc-down  # Stop the fsc-infra containers\nmake fsc-clean # Wipe everything: containers, images, CA material\n```\n\n## Real FSC end-to-end\n\nThree FSC orgs run alongside the main stack:\n\n- **Consumer-org (mortgage lender)** — consent-flow consumer; provider claims resolve to `flow=dvtp:query`, `subject_id_type=pseudonym`\n- **EDI-Issuer** — wallet-flow consumer; provider claims resolve to `flow=eudi:attestation`, `subject_id_type=direct`\n- **Provider (source-holder)** — provides the `bri` service; endpoint routes through the bron-sidecar\n\n`make demo` orchestrates the full sequence automatically:\n- PKI generation (root-CA + per-org certs)\n- FSC-infra start (three orgs + directory-peer)\n- Contract seed (bri-service + publication + two connection contracts + grant-links)\n- Main stack with dienstverlener-backend, eudi-adapter, pdp-service, bron-sidecar\n\nStep-by-step targets are available for debugging:\n\n1. **`make fsc-all-up`** — FSC-infra + orgs. The directory-manager runs with `--auto-sign-grants=servicePublication`; the provider-manager runs with `--auto-sign-grants=serviceConnection`. Contracts reach `CONTRACT_STATE_VALID` without manual review.\n\n2. **`make fsc-seed-bri`** + **`bash fsc-infra/scripts/seed-bri-connection-hv.sh`** — services + contracts + grant-links. Registers the `bri` service in the provider-Controller (endpoint = bron-sidecar), posts publication + two connection contracts, and upserts the grant-links per consumer. The provider Manager obtains flow and identifier semantics from `additional-claims-service` when issuing an access token. Idempotent.\n\n   Grant-link upsert goes via direct SQL — v2.4.0 has no REST endpoint for grant-link CRUD.\n\n3. **Generate pdp-service TLS cert + restart**:\n   ```bash\n   bash fsc-infra/pki/generate-pdp-cert.sh\n   docker compose up -d --force-recreate dienstverlener-backend eudi-adapter pdp-service graphql-server bron-sidecar\n   docker compose -f fsc-infra/docker-compose.yml up -d --force-recreate bd-inway\n   ```\n\n   `generate-pdp-cert.sh` produces a self-signed cert (SAN=`pdp-service`) — FSC-Inway's AuthZen plugin requires HTTPS+CA. The same `.pem` is mounted by the provider-inway as `AUTHZEN_ROOT_CA`.\n\n**Reset**:\n\n```bash\nmake fsc-down\ndocker volume rm fsc-infra_postgres-data    # wipe all contracts/publications\n```\n\nWithout a volume wipe, contracts and grant-links survive a restart.\n\n## EUDI public reachability\n\nThe EUDI flow needs two publicly-reachable HTTPS URLs:\n\n- `EUDI_PUBLIC_URL` — the wallet on a phone opens this to talk to the `issuance-server`.\n- `EUDI_BRI_URL` — the `issuance-server` fetches attestations from the `eudi-adapter` at this URL.\n\nBoth values are read from `.env`. Pick whichever way to expose the two services fits your setup:\n\nThe developer-portal container writes `EUDI_PUBLIC_URL` to\n`/runtime-config.js` when it starts. This allows Kubernetes and other runtime\nenvironments to configure wallet QR links without rebuilding the frontend.\nFor backwards compatibility, the container also accepts\n`VITE_EUDI_PUBLIC_URL`.\n\n**(a) Own domain / reverse proxy** — point two HTTPS hostnames at the compose ports and set the URLs. Nothing else to install.\n\n**(b) Cloudflare named tunnel (bundled)** — one Cloudflare tunnel with two Public Hostnames configured in the dashboard, plus the connector token in `.env`:\n\n```bash\n# In .env\nCLOUDFLARE_TUNNEL_TOKEN=eyJ...\nEUDI_PUBLIC_URL=https://eudi-is.your-cf-hostname.tld/\nEUDI_BRI_URL=https://eudi-bri.your-cf-hostname.tld/\n\n# Start the tunnel alongside the EUDI stack\ndocker compose -f docker-compose.yml -f docker-compose.cloudflare-tunnel.yml --profile eudi up -d\n```\n\n**(c) Ad-hoc tunnel** (ngrok, `cloudflared --url`, `tailscale funnel`, …) — start it yourself, paste the two URLs into `.env`, then bring up the stack without the tunnel file.\n\n## Testing\n\n```bash\n# Go happy-path integration tests (per service)\nfor svc in additional-claims-service bron-sidecar bsnk-mock consent-portal-backend consent-register \\\n           dev-portal-backend dienstverlener-backend eudi-adapter \\\n           graphql-server pdp-service sector-pip; do\n  (cd services/$svc \u0026\u0026 go test -timeout 60s ./...)\ndone\n\n# OPA policy unit tests\ndocker compose exec opa opa test /policies -v\n```\n\nCI (`.github/workflows/ci.yml`) runs both on every PR.\n\n## Release\n\nPushing a SemVer tag with a `v` prefix starts `.github/workflows/release.yml`.\nThe workflow builds 13 application images for `linux/amd64` and `linux/arm64`\nand builds `eudi-issuance-server` for `linux/amd64`. It publishes them to GHCR\nwith a tag without the `v` prefix plus `latest`, and then publishes the generic\n`gbo-app` Helm chart with the same normalized version.\nThe Compose development flow is unchanged.\n\nThe `eudi-issuance-server` build uses the pinned `vendor/nl-wallet` submodule.\nClone or update this repository with submodules enabled before building it\nlocally:\n\n```bash\ngit submodule update --init --recursive\n```\n\nCreate a release after the release changes are on `main`:\n\n```bash\ngit tag v0.1.0\ngit push origin v0.1.0\n```\n\nThe resulting image and chart versions are `0.1.0`:\n\n```bash\ndocker pull ghcr.io/ictu/gbo-demo/bsnk-mock:0.1.0\nhelm pull oci://ghcr.io/ictu/gbo-demo-charts/gbo-app --version 0.1.0\n```\n\nGHCR packages are private by default. After the first publication, set the 14\nimage packages and `gbo-demo-charts/gbo-app` to **Public** in the organization\npackage settings before testing anonymous pulls from tenant clusters.\n\nFor a local chart smoke test:\n\n```bash\nhelm lint deploy/helm/gbo-app\nhelm template bsnk-mock deploy/helm/gbo-app \\\n  --set image.repository=ghcr.io/ictu/gbo-demo/bsnk-mock \\\n  --set image.tag=0.1.0 \\\n  --set containerPort=4003 \\\n  --set healthPath=/health\n```\n\nThe chart also supports overriding the container entrypoint, passing arguments,\nmounting native Kubernetes volumes, and selecting the health probe scheme. The\nexample values files cover an OPA policy ConfigMap and a TLS-enabled PDP service:\n\n```bash\nhelm template opa deploy/helm/gbo-app \\\n  --values deploy/helm/gbo-app/examples/opa-values.yaml\nhelm template pdp-service deploy/helm/gbo-app \\\n  --values deploy/helm/gbo-app/examples/pdp-service-values.yaml\n```\n\n## Troubleshooting\n\n**Services not starting?**\n```bash\ndocker compose logs \u003cservice-name\u003e\n```\n\n**OPA returning unexpected results?**\n```bash\n# Check OPA decision logs\ndocker compose logs opa | grep \"decision\"\n\n# Test OPA directly\ncurl -X POST http://localhost:9181/v1/data/dvtp/authz -d '{\"input\": {...}}'\n```\n\n**Frontend not loading?**\n- Check the three frontends (`dienstverlener-mock` :9001, `toestemmingsportaal-frontend` :9002, `developer-portal` :9003) and their backends.\n- `docker compose logs \u003cservice\u003e` for the container in question.\n\n## Adding new access flows\n\nThe architecture is designed for incremental extension. Every new flow shares the same FSC-Inway → pdp-service (AuthZen) → OPA → bron-sidecar → GraphQL chain. Flow-specific context is provider-owned: OpenFSC asks `additional-claims-service` during token issuance and signs the returned values into the access token's `add` claim.\n\nThe checked-in mapping is deliberately small demo policy, not a second production contract register. A production deployment should resolve these claims from the same authoritative onboarding or authorization source that governs the relationship.\n\n- **Legal-basis (gov-to-gov)**: add `policies/legal-basis/*.rego`, add a new FSC consumer org and configure provider claims with `flow=g2g:legal-basis`; the PDP dispatches on the signed token claim.\n- **Wallet flow (already implemented)**: see `eudi-adapter`.\n- **AS4 / SDG-OOTS**: add an AS4 bridge mock + Domibus mock.\n\n## Repository owner\n\nOwner: **Jeroen de Kok** — \u003cjeroen.dekok@ictu.nl\u003e\n\nPlease open an issue for questions or feature requests. For security issues see [SECURITY.md](SECURITY.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fictu%2Fgbo-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fictu%2Fgbo-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fictu%2Fgbo-demo/lists"}