Overview
Promulga is a small web application (Python, FastAPI) packaged as Docker containers:
| Container | Role |
|---|---|
app | portal (/) and workspace (/admin), database (SQLite by default, PostgreSQL optional), background jobs (e-mails, reminders, exports) |
gotenberg | converts Word / LibreOffice files to PDF at publication |
caddy | reverse proxy with automatic HTTPS (Let's Encrypt) |
onlyoffice | optional online editor (profile editor) |
All data (database, files, published PDFs, signing key) lives in the Docker volume promulga-data. Source code and full documentation: GitHub.
Installation
Follow the download page for Windows, macOS or Linux. In short:
curl -L -o promulga.zip https://github.com/OlivierBouchezDataGovernance/promulga/releases/latest/download/promulga-docker.zip
unzip promulga.zip -d promulga && cd promulga
./deploy/setup.sh && nano .env
docker compose pull && docker compose up -d
docker compose logs app | grep set-password
The last command prints a one-time link (valid 24 h) to choose the password of the first administrator (ADMIN_EMAIL).
Server with a domain name (HTTPS)
- Create a Linux VM (2 vCPU, 4 GB RAM; Ubuntu 24.04), open ports 80 and 443, and give it a fixed IP address.
- Point a DNS record (
A) to it, e.g.docs.example.org. - In
.env:SITE_DOMAIN=docs.example.org,BASE_URL=https://docs.example.org,SECURE_COOKIES=true. docker compose up -d: Caddy obtains the certificate automatically.
A complete step-by-step guide for a Google Cloud VM is in INSTALL.md. Two sites (portal and workspace on separate host names) and an intranet-only portal are possible with PUBLIC_HOST, ADMIN_HOST and PORTAL_ALLOWED_IPS.
Configuration (.env)
| Variable | Purpose |
|---|---|
SITE_DOMAIN, BASE_URL | host name(s) served with HTTPS; public address used in e-mails |
ADMIN_EMAIL | first administrator (one-time link in the logs) |
REF_PATTERN | document reference, default {TYPE_CODE}-{ID} (POL-0007) |
FILENAME_PATTERN | file names, e.g. {CODE:domain}-{TYPE_CODE}-{VERSION}-{SUBVERSION}-{TITLE} |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_FROM | outgoing e-mail; the password goes in secrets/smtp_password |
OIDC_ISSUER, OIDC_CLIENT_ID | single sign-on (Entra ID, Google, Keycloak...); secret in secrets/oidc_client_secret |
EXPORT_TAG, GITHUB_REPO, GITHUB_FOLDER | public export; token in secrets/github_token |
ONLYOFFICE_URL | /onlyoffice to enable the online editor |
SIGN_PDFS | electronic seal (PAdES) of published PDFs |
Everything visual and functional (colours, logo, banner and footer HTML, deadlines, reminders, document types, portal access) is set in the application, Admin > Settings.
Secrets and security
- No password in
.env: each secret is a file insecrets/(mode 600), mounted as a Docker secret; it never appears indocker inspect. ./deploy/check.shverifies the permissions of.envandsecrets/and warns about passwords left in.env.- Admin > Security lists the checks the application can do itself: HTTPS, temporary passwords, secrets, backups, administrators without passkey or SSO.
- Recommend passkeys to administrators (My account > Passkeys).
First steps in the application
- Settings > Appearance: site name, colours, logo, banner and footer HTML.
- Settings > Deadlines and alerts: stage durations (default 28/14/14/14 days), reminders, periodic review, document types (
Name=PREFIX=icon), Word template. - Tags: taxonomies (Domain, Regulation, Audience, Visibility) and their codes; the tag
publicmarks documents for the public export. - Users: invite Document Managers and contributors (participants of a document are invited automatically).
- + New document: title, type, owner, reviewers, approvers, tags; the owner receives an e-mail.
Roles
| Role | Can |
|---|---|
| viewer | read the portal when it requires a sign-in |
| contributor | be owner, reviewer or approver of documents (counts as an editor) |
| manager | Document Manager: create documents, change participants, close a stuck review, start new revisions, withdraw, tags, audit log, indicators |
| admin | manager + users, settings, outbox, export, API tokens, security |
Word templates, cover page and file names
For .docx files Promulga inserts a locked cover page (metadata and version history) and refreshes variables written as {{TITLE}}, {{VERSION}}, {{STATUS}}... at every check-in, download and publication. Other formats show a "limited capability" warning; their published PDF still gets a generated cover page. Details: WORD-TEMPLATES.md.
Online editor (OnlyOffice)
Set ONLYOFFICE_URL=/onlyoffice and start with docker compose --profile editor up -d. Owners edit in Draft and Revision; reviewers comment and suggest changes in the same single version during Review; everybody else reads. The Community Edition allows up to 20 simultaneous editors.
Export, API, webhooks, seal
- Public export (Admin > Export): index page, PDFs, DCAT-AP / JSON / CSV catalogues and checksums of the documents tagged
public; zip download or one-commit push to GitHub (fine-grained token, Contents: read and write on one repository). - REST API (Admin > API): read-only tokens;
GET /api/v1/documents,/api/v1/documents/{ref},/api/v1/taxonomies. - Webhooks (Settings > Integrations): Microsoft Teams, Slack or any JSON endpoint at each workflow event.
- Electronic seal:
SIGN_PDFS=true, with a self-signed certificate or your organisation's.
Details: INTEGRATIONS.md.
Backup, update, restore
# back up (database + files) inside the data volume, then copy it out
docker compose exec app promulga backup
docker compose cp app:/data/backups ./backups
# update: back up, pull the new images, restart (the database is migrated automatically)
docker compose pull && docker compose up -d
# restore an archive (replaces the current data)
docker compose stop app
docker compose run --rm app promulga restore /data/backups/promulga-backup-<date>.tar.gz --yes
docker compose up -d
Schedule the backup (cron) and keep copies off the server. Other commands: promulga reminders, promulga flush-outbox, promulga export --github, promulga create-admin [email protected] (one-time link for a lost administrator password).
Troubleshooting
- Cannot sign in over HTTP: set
SECURE_COOKIES=falsefor a local test only. - E-mails not sent: Admin > Outbox shows the error; check
SMTP_*andsecrets/smtp_password(cloud providers block port 25: use 587 or 465). - Publication fails: the PDF converter (
gotenberg) must run; or check in a PDF. - Port 80 in use:
HTTP_PORT=8080andBASE_URL=http://localhost:8080. - Logs:
docker compose logs --tail 200 app. Questions: [email protected] (best effort).