Help us shape the future of Go! Take the 2026 Go Developer Survey

ce

command
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

README

Records data-owner consent for data access requests and runs the consent lifecycle: a consumer asks, the citizen approves or rejects in the Consent Portal, and the grant expires or is revoked later.

Overview

flowchart LR
    OE[Orchestration Engine] -->|create and check consent<br/>internal API, no auth| CE
    CP[Consent Portal] -->|list, view, decide<br/>portal API, JWT| CE
    CE --> DB[(PostgreSQL: consent_engine)]
    CE -.->|fetch signing keys| IDP[ThunderID]
Caller Uses
Orchestration Engine Internal API: creates a consent when the PDP says the owner must consent, and reads its status
Consent Portal (portals/apps/consent) Portal API with the citizen's access token: list, view, approve, reject, revoke

The only outbound call is the identity provider's JWKS endpoint, used to verify portal tokens. The CE does not emit audit events.

Quick start

With Docker Compose (from the repo root; starts PostgreSQL and ThunderID first, and loads the schema on a fresh database):

docker compose up --build consent-engine
curl http://localhost:8081/internal/api/v1/health

Native, against a PostgreSQL with an empty database. The service does not create tables, so load the schema first:

PGPASSWORD=<password> psql -h localhost -U postgres -d consent_engine \
  -v ON_ERROR_STOP=1 -f cmd/ce/schema.sql

cp cmd/ce/config.example.yaml cmd/ce/config.yaml    # once; config.yaml is git-ignored
export DB_PASSWORD=<password>
make run consent-engine    # or: go run ./cmd/ce -config cmd/ce/config.yaml

config.example.yaml points at localhost:5432 as user postgres, reads the password from DB_PASSWORD, and validates tokens from a ThunderID on localhost:8090. Schema changes go directly into schema.sql; there is no versioned upgrade path.

Configuration

The CE reads one YAML file: the -config flag, else CONFIG_PATH, else config.yaml in the working directory. There are no other flags or environment variables. The Docker image sets CONFIG_PATH=/app/config/config.yaml and has no config file of its own, so mount one there. Compose mounts config.docker.yaml.

A value written as "{{env:NAME}}" or "{{file:/path}}" is read from that environment variable or file at startup, so secrets stay out of the file. The placeholder must be the whole value, and an unset or empty variable stops the service.

Key Default Notes
server.port 8081 Listen port. The server binds on all interfaces.
server.timeout 10s Read and write timeout.
logging.level info debug, info, warn or error.
logging.format text text or json.
consentPortalURL none Required. Must include scheme and host. Consent links are <url>?consentId=<uuid>. Its origin is always allowed.
cors.allowedOrigins empty Origins allowed besides consentPortalURL. * stops the service at startup while allowCredentials is true.
cors.allowedMethods, cors.allowedHeaders see config.go A list in the file replaces the default list.
cors.allowCredentials, cors.maxAge true, 86400
idp.jwksURL none Required. Signing keys for portal tokens. If unreachable, every portal call returns 401.
idp.issuer, idp.audience none Required. Expected token iss and aud. Compose uses ThunderID's base URL and CONSENT_PORTAL.
idp.subjectClaim sub Token claim whose value must equal the consent's ownerId. Compose sets nic. A token without it gets 401.
idp.insecureSkipTLSVerify false Skips TLS verification for the JWKS fetch. Development only.
database.driver postgres Only postgres is supported.
database.postgres.host, .user, .password none Required.
database.postgres.port 5432
database.postgres.name consent_engine
database.postgres.sslMode require Use disable for a local PostgreSQL without TLS.
database.postgres.pool.maxOpenConns, .maxIdleConns, .maxConnLifetimeSeconds 25, 5, 3600 Connection pool.
database.idleTimeout 30m Closes connections idle this long.

The service checks the file at startup and exits with an error naming the bad key. Keys it does not know are ignored, so check the spelling of any key that seems to have no effect.

API

Full schemas: openapi.yaml. Errors are {"error": {"code": "...", "message": "..."}}, except a portal call with a missing or invalid token, which gets the shared authentication layer's 401 body, {"error": "unauthorized", "message": "..."}. Token checks: CONSENT_LIFECYCLE.md.

Internal API (no authentication, for the Orchestration Engine):

Method and path Purpose
POST /internal/api/v1/consents Create a consent (201). Body: appId, consentRequirement {owner, ownerId, fields[]}, optional appName, grantDuration, consentType.
GET /internal/api/v1/consents?ownerId=&appId= Latest consent for the pair. Both parameters are required (400); 404 if none.
GET /internal/api/v1/health {"status": "healthy"}

Portal API (Bearer access token; the token's subject claim must equal the consent's ownerId):

Method and path Purpose
GET /api/v1/consents The caller's consents, newest first. status (repeatable or comma-separated), limit (1-100, default 20), offset.
GET /api/v1/consents/{consentId} One consent. 403 if it belongs to someone else.
PUT /api/v1/consents/{consentId} Body {"action": "approve" | "reject" | "revoke"}. 409 if the consent is not in a state that allows the action.
GET /api/v1/health Public, no token needed.

System: GET /health and GET /metrics (Prometheus), both unauthenticated.

How it works

  • A consent is pending until the owner approves or rejects it, or the request times out (1 hour for realtime, 24 hours for offline).
  • Approving starts a grant of the requested duration (default 1 hour). A grant can be revoked.
  • At most one pending or approved consent exists per (ownerId, appId). Creating the same request again returns the existing one; creating it with different fields revokes the old one and issues a new one.
  • Expiry is applied lazily, when a consent is read or acted on.

States, durations and the exact create rules: CONSENT_LIFECYCLE.md.

Security and deployment

Deployment security: network isolation. The /internal/api/v1/*, /health and /metrics routes are not authenticated. Anyone who can reach them can create consents and read consent status. Expose only the portal routes (/api/v1/*) to the Consent Portal's network, and keep the rest reachable only by the Orchestration Engine and your monitoring.

Development

make validate-test consent-engine
go test ./cmd/ce/... ./internal/ce/... ./internal/database/... -count=1
make quality-check consent-engine

Service tests use an in-memory SQLite database. The PostgreSQL connection and bootstrap tests are skipped unless TEST_DATABASE_URL points at a disposable database (the bootstrap test also needs psql and permission to create databases):

TEST_DATABASE_URL='postgres://postgres:password@localhost:5432/consent_test?sslmode=disable' \
  go test ./cmd/ce/... ./internal/database -count=1 -v

Docker image: docker build -t consent-engine -f cmd/ce/Dockerfile . from the repo root. The configuration is in config.go. Code is in internal/ce: router, middleware, handlers, services, models. CORS is OpenNSW/core/cors.

More docs

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL