Configuration
Every environment variable a self-hosted deployment reads, grouped by area, with its default and what it does.
The API is configured only through environment variables, all listed here.
The API checks them at start-up. An error stops it with a Config error: line in the log. A warning is logged as Config warning:. The product sends no email or SMS, so there are no mail settings.
Values that must never change#
Four values are part of the stored data. A change raises no error at start. It breaks what was recorded before.
| Variable | What breaks if it changes |
|---|---|
ENCRYPTION_KEY |
Every encrypted field becomes unreadable: readable identifiers, rights request contacts, webhook signing secrets, identity secrets, fingerprint keys and translation keys. Every secret key (sk_…) is refused. Open confirmation, unsubscribe and consent portal links stop working. Values derived from it change too |
SUBJECT_HASH_PEPPER |
No existing record or rights request can be found by identifier. Returning visitors are treated as new |
EVIDENCE_CHAIN_KEYS |
Every event sealed with a removed key fails verification. Adding keys is safe |
RECEIPT_SIGNING_PRIVATE_KEY |
Receipts and anchors signed with the old key no longer pass the console and API checks |
An empty value is also a value
While SUBJECT_HASH_PEPPER, EVIDENCE_CHAIN_KEYS or RECEIPT_SIGNING_PRIVATE_KEY is empty, the API takes it from another secret. A different value later is a change. Set all of them before the first consent is recorded. To give a running deployment dedicated values, see Rotate secrets.
Back these values up with the same care as the database.
Server#
| Variable | Required or default | What it does |
|---|---|---|
NODE_ENV |
Unset. The supplied image sets production |
production turns on the production checks. The API refuses to start without ENCRYPTION_KEY or PUBLIC_API_URL. The console API refuses localhost origins, demo data and private scan targets |
PORT |
3003 |
Port the API listens on. The image's health check expects 3003 |
LOG_LEVEL |
log |
error, warn, log or debug |
The API also reads a .env file from its working directory, if one exists.
Database#
| Variable | Required or default | What it does |
|---|---|---|
DB_HOST |
localhost |
PostgreSQL host |
DB_PORT |
5432 |
PostgreSQL port |
DB_USERNAME |
postgres |
User. An empty string is an error |
DB_PASSWORD |
postgres. Required by the compose file |
Password. An empty string is an error |
DB_DATABASE |
piedap_consent |
Database name. An empty string is an error |
DB_MAX_CONNECTIONS |
20 |
Pool size of each API replica. Size the database for replicas times this number |
DB_LOGGING |
false |
true logs every SQL statement. Statements can hold hashes and consent data, so keep it off in production |
DB_SYNCHRONIZE |
false |
Lets the ORM alter the schema to match the code. Leave it unset. Migrations own the schema |
The API applies pending migrations when it starts. It tries the database connection ten times, three seconds apart, then exits. The database user must be allowed to create the uuid-ossp extension on first start.
Overview dashboards#
The charts of the console's Overview query the database directly. These two settings keep them from crowding out consent writes.
| Variable | Required or default | What it does |
|---|---|---|
DASHBOARD_STATEMENT_TIMEOUT_MS |
5000 |
Time limit, in milliseconds, for a chart query that reads single records or events. Kept between 1000 and 15000. Other chart queries stop after 3000 |
DASHBOARD_MAX_STATEMENTS |
30% of DB_MAX_CONNECTIONS, at most 6, at least 2 |
Chart queries one API replica runs at once, kept between 2 and half of DB_MAX_CONNECTIONS. Single-record and event queries may use all but two, and at least one |
Defaults and limits lists the other limits on chart queries.
Redis#
| Variable | Required or default | What it does |
|---|---|---|
REDIS_HOST |
localhost. Required for the console |
The Redis that IAM writes sessions to. It also shares rate-limit counters between replicas |
REDIS_PORT |
6379 |
Redis port |
REDIS_PASSWORD |
None | Redis password |
Without Redis the SDK surface still works and each process counts rate limits by itself. Nobody can sign in to the console.
Secrets and keys#
| Variable | Required or default | What it does |
|---|---|---|
ENCRYPTION_KEY |
Required | Encrypts stored secrets with AES-256-GCM, and salts secret-key hashes and link tokens. It is the fallback source for the keys below. Any long random string |
SUBJECT_HASH_PEPPER |
ENCRYPTION_KEY. Recommended |
Key of the HMAC-SHA256 hash of every identifier, and salt of the IP address hash |
EVIDENCE_CHAIN_KEYS |
Derived from ENCRYPTION_KEY. Recommended |
Keys that seal consent events, as id:secret,id:secret. An id is 1 to 40 letters, digits, _ or -. A secret is at least 32 characters with no comma |
EVIDENCE_CHAIN_ACTIVE_KEY |
The last key listed | Id of the key that seals new events. It must be one of the listed ids |
RECEIPT_SIGNING_PRIVATE_KEY |
Derived from RECEIPT_SIGNING_KEY, else ENCRYPTION_KEY. Recommended |
Ed25519 key that signs receipts and daily anchors. A PKCS#8 PEM, where \n may stand for line breaks, or a base64 32-byte seed |
RECEIPT_SIGNING_KEY_ID |
ed plus the first ten hex characters of the SHA-256 hash of the public key |
Published as the key's kid at /sdk/v1/keys and written on receipts and anchors |
RECEIPT_SIGNING_KEY |
ENCRYPTION_KEY. Recommended |
Checks receipts issued in the earlier HMAC format. It is the source of the derived signing key and chain key when those are not set |
EVIDENCE_SIGNING_KEY |
None | An older source of the chain key, used when EVIDENCE_CHAIN_KEYS is empty. Leave it unset on a new deployment |
In production the API warns when SUBJECT_HASH_PEPPER or RECEIPT_SIGNING_KEY is not set. In every mode it refuses to start when the chain keys or the signing key are malformed, or when no secret exists to derive them from.
IAM#
The console has no accounts of its own. These values must match the PIEDAP IAM deployment that signs people in.
| Variable | Required or default | What it does |
|---|---|---|
LOGIN_COOKIE |
None. Required for the console | Name of the session cookie IAM sets |
LOGIN_SECRET |
None. Required for the console | Secret that verifies the signature of the session cookie |
COOKIE_DOMAIN |
None. Required for the console | Domain of the IAM cookies. The API clears them on sign-out or a failed check; a wrong value leaves a stale cookie |
VERIFY_LOGIN_COOKIE |
None | Name of another IAM cookie to clear on sign-out |
REFRESH_COOKIE |
None | Name of the IAM refresh cookie to clear on sign-out |
BASE_IAM_URL |
None. Required for API tokens | Address of the IAM service. The API calls it to check a bearer token sent to the console API |
SERVER_SIDE_API_KEY |
None. Required for API tokens | This service's own key, sent to IAM with that check |
IAM_LOGIN_URL |
None. Recommended | Where the console sends someone who is not signed in |
Public URLs and proxies#
| Variable | Required or default | What it does |
|---|---|---|
PUBLIC_API_URL |
http://localhost:3003. Required in production |
The public address of the deployment, with no trailing slash. Built into install snippets, confirmation links, hosted page links, and the issuer and key address on receipts |
RUNTIME_CDN_URL |
PUBLIC_API_URL |
Base address of the runtime files in install snippets, when you serve them from a CDN |
UI_URL |
None | An origin allowed to call the console API with the session cookie |
ADMIN_UI_URL |
None | A second such origin |
TRUST_PROXY |
loopback, linklocal, uniquelocal |
Which proxies may set X-Forwarded-For: a hop count such as 2, true, false, or a list of addresses and ranges |
- The console API always accepts
httpsorigins one level underpiedap.com,piedap.inandpiedap.io, such ashttps://app.piedap.io. - It accepts
http://localhostandhttp://127.0.0.1with a port whenNODE_ENVis notproduction. - The SDK surface accepts any origin and relies on each key's allowed origins.
- If a CDN sets a country header such as
CF-IPCountryorCloudFront-Viewer-Country, the API uses it. Otherwise it looks the address up in its own database.
Do not set TRUST_PROXY to true on a public port
With true, any client can choose its own address through X-Forwarded-For. That address picks its rate-limit bucket and is written to the audit log.
Rate limits#
| Variable | Required or default | What it does |
|---|---|---|
RATE_LIMIT_MAX |
300 |
Requests per window from one address to the console API |
RATE_LIMIT_SDK_MAX |
3000 |
Requests per window from one address to the SDK surface |
RATE_LIMIT_WINDOW_MS |
60000 |
Length of that window in milliseconds |
SDK_WRITES_PER_ADDRESS_PER_MINUTE |
60 |
Writes per minute from one address with one publishable key |
SDK_CRITICAL_CALLS_PER_MINUTE |
60 |
Calls on a person as a whole (find, export, withdraw, erase, link), notice campaign links and processor tasks, per minute with one secret key |
SDK_CHECK_MAX_PER_MINUTE |
6000 |
The most identifiers a minute any key may be allowed to check with consent:check. Lowering it also caps keys set higher |
Each key also has its own limit per minute, stored with the key: 600 for a test key and 6,000 once it is live. /health and /metrics are not rate limited. What a caller sees is in Rate limits.
Evidence and retention#
| Variable | Required or default | What it does |
|---|---|---|
ENFORCE_EVIDENCE_RETENTION |
false |
true lets the daily retention job delete records whose consent ended longer ago than the project's Keep evidence for period. This cannot be undone. false deletes nothing |
STORE_SUBJECT_REFERENCE |
false |
Fallback when a project's settings cannot be read: keep an encrypted readable copy of the identifier |
PROJECT_DELETION_DAYS |
30 |
Days from scheduling a project's closure to deleting its data, 1 to 365. See Close a project |
The job is described in Scheduled jobs.
Consent import#
| Variable | Required or default | What it does |
|---|---|---|
CONSENT_IMPORT_MAX_ROWS |
100000 |
Rows in one file for Import CSV |
CONSENT_IMPORT_DAILY_ROWS |
100000 |
Records console imports may write in one project in one UTC day |
Audit log#
| Variable | Required or default | What it does |
|---|---|---|
AUDIT_LOG_STDOUT |
true |
false stops the one-line JSON copy of each audit entry on standard output |
AUDIT_FEED_LAG_SECONDS |
60 |
Entries younger than this are held back from GET /sdk/v1/audit-log. From 5 to 3600 |
AUDIT_FEED_CALLS_PER_MINUTE |
60 |
Feed calls per minute with one key, on top of the key's own budget |
Webhooks and outbound calls#
| Variable | Required or default | What it does |
|---|---|---|
PRIVATE_NETWORK_ALLOWLIST |
Empty: no private network is reachable | Private networks that webhooks may reach when the endpoint's Private network switch is on, and that the translation service may reach. Comma-separated CIDR ranges, addresses and host names |
ALLOW_PRIVATE_WEBHOOK_URLS |
Not read | Replaced by PRIVATE_NETWORK_ALLOWLIST. With true, the API refuses to start in production and warns elsewhere |
PRIVATE_NETWORK_ALLOWLIST=10.20.0.0/16,api-gw.bank.internal,*.crm.bank.internal- A bare address means that one address.
*.crm.bank.internalmatches its subdomains, not the name itself. - A range wider than
/8is refused, and so is an entry that is none of these. In production that stops start-up; elsewhere it is a warning. 169.254.0.0/16, where cloud metadata services answer, is never reachable, even when listed.- In production, a loopback entry or
localhoststarts with a warning: a webhook could reach services on the API host itself. - For a receiver on your own machine in development:
127.0.0.1/32,::1/128,localhost.
Translation#
| Variable | Required or default | What it does |
|---|---|---|
SARVAM_API_KEY |
None | The deployment's own Sarvam key for machine translation of notice wording. Without it, each project adds its own key or chooses another service in Settings |
Keys that projects add for a translation service are stored in the database, encrypted with ENCRYPTION_KEY.
Platform key#
| Variable | Required or default | What it does |
|---|---|---|
PLATFORM_API_KEYS |
None | One key per operator, as comma-separated name:sha256 entries, where sha256 hashes that person's key. Their calls record their name. See Approve live keys |
PLATFORM_API_KEY |
None | One key the operator's team shares, sent as X-Piedap-Platform-Key. With neither set, the endpoints that approve live keys and set DPDP dates answer 503 |
Hosting location#
| Variable | Required or default | What it does |
|---|---|---|
DEPLOYMENT_REGION |
None | Where the data is kept, in words, such as India (Mumbai) |
DEPLOYMENT_COUNTRY |
None | That country's ISO 3166-1 alpha-2 code, such as IN. Another value stops start-up in production. Unset, production logs a warning |
PRODUCT_SCOPE |
india |
india offers only the DPDP Act and hides unused IAB and opt-out settings. global offers every law |
DEPLOYMENT_PROVIDER |
None | The hosting provider |
Every project sees them in Settings and in the DPDP Act check.
Monitoring#
| Variable | Required or default | What it does |
|---|---|---|
METRICS_TOKEN |
None | Bearer token for /metrics. While unset, /metrics answers only clients on loopback or a private network, and 404 to everyone else |
Runtime features#
| Variable | Required or default | What it does |
|---|---|---|
DISCOVERY_SAMPLE_RATE |
0.05 |
Share of page views on which the browser runtime reports cookie and storage names |
TCF_GVL_URL |
https://vendor-list.consensu.org/v3/vendor-list.json |
Where the IAB Global Vendor List is downloaded from. Point it at a mirror if the API has no outbound access |
GVL_CACHE_DIR |
piedap-consent-gvl in the system temp directory |
Where the vendor list is cached |
SCANNER_CHROMIUM_PATH |
The browser Playwright finds. The supplied image sets /usr/bin/chromium-browser |
Chromium used for site scans |
Development only#
Never set these on a deployment that holds real consent.
| Variable | Default | What it does |
|---|---|---|
AUTH_BYPASS |
false |
Skips sign-in and Redis and uses a built-in session. The API refuses to start with it unless NODE_ENV is development or test |
SEED_ON_BOOT |
false |
Adds starter purposes and a draft form to one project at start |
SEED_ORGANIZATION_ID, SEED_ACCOUNT_ID, SEED_PROJECT_ID |
Fixed development ids | The project SEED_ON_BOOT fills |
ALLOW_DEMO_DATA |
false |
Allows the demo data generator when NODE_ENV is production |
ALLOW_PRIVATE_SCAN_TARGETS |
false |
Lets scans target private addresses. Ignored when NODE_ENV is production |
Build settings#
The API does not read these. They are used when you build the images.
| Setting | Where | Default | What it does |
|---|---|---|---|
INSTALL_SCANNER |
Build argument of the API image | true |
false leaves Chromium out for a smaller image. Scans then fail |
VITE_BASE_PATH |
Build argument of the console image | / |
Path prefix the console is served under, such as /consent/ |
VITE_DOCS_URL |
Build argument of the console image | Empty | Adds a Documentation link to the Install page. Empty hides it |
VITE_PIEDAP_CONSENT_API_URL |
Console build | The console's own origin | Address of the API when the console is loaded from another origin |
VITE_PIEDAP_CONSENT_HOME_PAGE_URL |
Module Federation build of the console | http://localhost:5176 |
Where remoteEntry.js is served from when the console loads inside the PIEDAP shell |
The console's nginx reaches the API as consent-api:3003 through Docker's DNS. Elsewhere, change web/nginx.conf.
Compose file#
docker-compose.yml passes only the variables it lists to the API container. It also sets DB_HOST, DB_PORT, REDIS_HOST and REDIS_PORT to its own containers, whatever .env says.
| Setting | Default | What it does |
|---|---|---|
CONSENT_DB_HOST_PORT |
5436 |
PostgreSQL's host port, on loopback |
DOCS_SITE_URL |
None | Address of the documentation |
DOCS_HOST_PORT |
8081 |
Its host port |
LAN_IP |
None | compose.lan.yml only: this machine's local network address, used as the demo's public address |
Variables it does not pass include TRUST_PROXY, ENFORCE_EVIDENCE_RETENTION, SARVAM_API_KEY, LOG_LEVEL and the rate limits. Set those, or your own database and Redis hosts, in an override file. Compose merges it automatically.
services:
consent-api:
environment:
REDIS_HOST: redis.example.in
SARVAM_API_KEY: ${SARVAM_API_KEY:-}
LOG_LEVEL: warn| Service | Published on | Note |
|---|---|---|
| Console | 0.0.0.0:8080 |
Put your reverse proxy in front. To bind it to loopback, set ports: !override ['127.0.0.1:8080:80'] in the override file (Compose 2.24.4 or later). A plain ports entry is added to this one |
| API | 127.0.0.1:3003 |
Loopback only. Use it for /metrics |
| PostgreSQL | 127.0.0.1:5436 |
Loopback only |
| Redis | 127.0.0.1:6379 |
Loopback only |
Data is kept in two named volumes, consent-pgdata and consent-redisdata. Docker lists them with the project name in front, as piedap-consent_consent-pgdata.
compose.lan.yml is for demos on a trusted network. It turns sign-in off, so never use it for a real deployment.