Open Pryv.io email configuration

Open Pryv.io sends two kinds of transactional email: the welcome message after account creation and the password-reset message after a reset request. Both are optional (disabled independently via services.email.enabled.*) and both share the same SMTP transport + Pug template pipeline.

v2 supports two delivery paths. Pick one at services.email.method in override-config.yml:

method Runs Templates live in Use when
in-process (recommended) The api-server worker that handled registration / reset-password PlatformDB (rqlite, cluster-wide) Single- and multi-core v2 deployments. No extra process, one less localhost hop, templates editable via CLI + admin API.
microservice (legacy) A separate service-mail process on each core bound to 127.0.0.1:9000 Disk files under templates/ on the service-mail box Existing deployments still running the standalone pryv/service-mail process.

Everything below assumes v2 (open-pryv.io). v1 deployments configured the same two delivery paths under services.email.method: mandrill | microservice in core/core/conf/core.json, with templates in a separate pryv/mail Docker container or via Mandrill’s hosted templates — that surface is no longer used in v2.

Table of contents

  1. Choose a method
  2. Common config — SMTP, sender, language
  3. in-process mode (recommended)
    1. Boot-time template seeding
    2. Managing templates — bin/mail.js CLI
    3. Managing templates — admin HTTP API
    4. PlatformDB keyspace
    5. Cluster propagation
  4. microservice mode (legacy)
  5. Template variables
  6. SPF record reminder

Choose a method

Set services.email.method in override-config.yml. Default is microservice for now; a future release will flip the default to in-process once both modes have had equal prod exposure.

services:
  email:
    method: in-process            # or 'microservice'
    enabled:
      welcome: true
      resetPassword: true

Common config — SMTP, sender, language

These apply to both methods. SMTP credentials and the sender identity are always per-core, local to override-config.yml — they do not propagate through PlatformDB.

services:
  email:
    defaultLang: en                # applied when the request has no `language` field
    welcomeTemplate: welcome-email
    resetPasswordTemplate: reset-password
    from:
      name: 'Pryv Lab no-reply'
      address: 'no-reply@your-domain.example'
    smtp:
      host: your-smtp-server
      port: 587
      secure: false
      auth:
        user: REPLACE_ME
        pass: REPLACE_ME

Need to use sendmail instead of SMTP for dev? Pass smtp: { sendmail: true, path: '/usr/sbin/sendmail' }nodemailer honours the same field names as the previous service-mail layer.

In-process mode renders Pug templates inside the api-server workers that already handle registration and password-reset. Templates live in the cluster-wide PlatformDB (rqlite) and propagate automatically to every core.

Boot-time template seeding

On the first boot with an empty PlatformDB, the master will seed templates from an on-disk Pug directory if you point it at one:

services:
  email:
    method: in-process
    templatesRootDir: /opt/open-pryv.io/mail-templates

Directory layout expected:

/opt/open-pryv.io/mail-templates/
├── welcome-email/
│   ├── en/
│   │   ├── subject.pug
│   │   └── html.pug
│   └── fr/
│       ├── subject.pug
│       └── html.pug
└── reset-password/
    └── en/
        ├── subject.pug
        └── html.pug

Seeding is idempotent: if PlatformDB already has any mail-template/* row the master skips the seed and logs the count. Re-seeding after the first boot is the job of the CLI / admin API below — changing the files on disk does NOT re-seed.

If templatesRootDir is empty, no templates are seeded. You can instead populate PlatformDB from scratch using the CLI templates seed --from <dir> or the PUT /system/admin/mail/templates/:type/:lang/:part admin route — both overwrite existing rows.

Managing templates — bin/mail.js CLI

Run from inside the open-pryv.io directory on any core:

node bin/mail.js templates list
# → type          lang  part     len
#   welcome-email en    html     482
#   welcome-email en    subject   18
#   ...

node bin/mail.js templates get welcome-email en html
# → prints the current Pug source to stdout

node bin/mail.js templates set welcome-email en html --file ./new-welcome.pug
# → overwrites the html part. CLI doesn't push IPC; sibling workers refresh
#   on their next request via the admin API path or the periodic cache re-read.

node bin/mail.js templates delete welcome-email fr        # wipes both parts for fr
node bin/mail.js templates delete welcome-email fr subject   # wipes only subject

node bin/mail.js templates seed --from /opt/open-pryv.io/mail-templates
# → OVERWRITES every row that exists on disk. Use this to bulk-replace.

node bin/mail.js send-test welcome-email en alice@example.com
# → renders the template with stub substitutions and sends a real email
#   through the configured SMTP transport.

Managing templates — admin HTTP API

All routes live under /system/admin/mail/ on every core and require the platform admin key (auth.adminAccessKey) as the Authorization header. Unauthorized requests return 404 (by design, to avoid advertising the surface).

Method Path Body Response
GET /system/admin/mail/templates { templates: [{type,lang,part,length}] }
GET /system/admin/mail/templates/:type/:lang/:part text/plain raw Pug
PUT /system/admin/mail/templates/:type/:lang/:part { "pug": "<source>" } 204
DELETE /system/admin/mail/templates/:type/:lang/:part 204
POST /system/admin/mail/send-test { type, lang, recipient } { sent: true }

PUT and DELETE both trigger a cross-worker refresh on the local core via IPC so the new content is live on the next request without a restart.

Example:

ADMIN_KEY=...
curl -sS -X PUT https://core-use1.example/system/admin/mail/templates/welcome-email/en/subject \
  -H "Authorization: $ADMIN_KEY" \
  -H "Content-Type: application/json" \
  --data-binary '{ "pug": "| Welcome to Example" }'

PlatformDB keyspace

Templates are stored as raw Pug strings under:

mail-template/<type>/<lang>/<part>

<part> is subject or html. The value is compiled on demand at render time; the compiled function is cached in the worker’s memory alongside the raw source. A refresh nudge (IPC broadcast or process restart) drops the compiled cache too.

Cluster propagation

microservice mode (legacy)

If you are still running the standalone pryv/service-mail process alongside each core, set:

services:
  email:
    method: microservice
    url: http://127.0.0.1:9000/sendmail/
    key: your-shared-auth-key        # matches http.auth on the service-mail side

Template variables

Pug templates receive a locals object at render time. The two shipped types expect:

welcome-email

Local Example
username alice
email alice@example.com

reset-password

Local Example
username alice
email alice@example.com
resetUrl https://sw.example.com/access/reset-password.html
resetToken 32-char opaque token

Use #{var} in Pug to interpolate.

SPF record reminder

Whichever method you pick, SMTP servers use SPF to verify the sender. If you’re emitting mail on behalf of your Pryv.io domain, add a TXT record to your DNS zone authorising your SMTP host:

@ 10800 IN TXT "v=spf1 include:spf.your-smtp-host.example ~all"

See DNS configuration for where to put it in your zone.