NASLAG

Documentatie

Overweegt u Paramant? Uw IT kan hier alles controleren. Abonnementen en prijzen staan op Prijzen.

API-referentie, SDK-handleiding, zelf hosten en compliance-documentatie voor PARAMANT v3.1.3. U hoeft de relay niet te vertrouwen: hij heeft nooit een sleutel om te ontsleutelen. De API voor Ondertekenen is beschikbaar vanaf Firm; zie prijzen.

Nieuw in 3.0.0

Versie 3.0.0 houdt het wire format, de API en de cryptografische garanties van 2.5.x gelijk, dus elke 2.5.x-client blijft werken. Deze versie richt zich op beheer en zelf hosten. De belangrijkste wijziging zit in de opbouw: de post-quantum bouwstenen staan nu in een eigen, auditbare bibliotheek.

  • Cryptografie apart in paramant-core (M5b). Het ondertekenen met ML-DSA-65 aan de serverkant gebeurt nu via paramant-core: een losse Rust-bibliotheek, klaar voor audit (BUSL-1.1), die de relay gebruikt via de NAPI-binding @paramant/core. ML-KEM-768 draait nog steeds aan de clientkant, in de browser of de SDK; de relay heeft nooit een sleutel. Zie Cryptografie.
  • Werkplek voor documenten. Vanuit het dashboard start u documentstromen, ziet u de ondertekenverzoeken van uw account en voert u acties als eigenaar uit, zonder dat er een nieuw vertrouwenspunt bijkomt.
  • Vernieuwde interface. Donkere modus (schakelaar rechtsboven), statusupdates in real time en een indeling die ook op mobiel werkt, in het hele dashboard.
  • Installatiewizard bij de eerste start. Een webwizard op /setup vervangt de handmatige beheerscripts voor nieuwe beheerders: admin-token, TOTP-registratie en de eerste sleutel, allemaal vanuit de browser (ADR R005).
  • Configuratie in het beheerpaneel. /admin/settings toont de relayconfiguratie in het paneel, zodat u voor gewone wijzigingen niet meer .env hoeft te bewerken en opnieuw te starten.
  • Beheer-CLI in de browser. /admin/cli is een webterminal voor foutopsporing door beheerders: u bekijkt de toestand van de relay zonder SSH-sessie.
  • Onderhandeling over de cryptomodus (ADR R006, aangenomen). /v2/capabilities meldt standaard een compacte core-modus (2 algoritmen); uitgebreide sets zet u zelf aan. Zie Cryptografie.
  • Architectuur voor add-ons (ADR R007, concept). Een specificatie voor integraties die in een eigen container draaien, zoals opslagspiegels, SIEM-exporters en SSO. Ze werken alleen met versleutelde gegevens en metadata, nooit met leesbare inhoud of sleutels.
  • Statische frontend (ADR R011). Met de optionele statische handler SERVE_FRONTEND serveert één relay zijn eigen interface, zodat zelf hosten direct werkt. Zie Zelf hosten.
  • Vastgelegde ontwerpkeuzes. De Architecture Decision Records R001 tot en met R011 leggen de keuzes achter 3.0.0 vast, van de hotfix-werkwijze tot de afsplitsing van de cryptografie. Ze staan in de repository onder docs/adrs/.

Snel starten

Verstuur uw eerste versleutelde bestand in minder dan 5 minuten. Maak een gratis account aan (beveiligd met TOTP; uw sleutel staat in het dashboard).

1

Installeer de SDK (Python 3.10+ of Node 18+; beide gebruiken dezelfde encoder voor wire format v1)

bash
# Python
pip install paramant-sdk

# JavaScript / Node
npm install paramant-sdk
2

Verstuur een bestand: het wordt aan uw kant versleuteld voordat het uw apparaat verlaat

python
from paramant_sdk import GhostPipe

gp = GhostPipe(api_key="pgp_your_key", device="laptop-01")
gp.receive_setup()  # register this device's keys (once)

# Send: returns (blob hash, inclusion proof). Share the hash with the receiver.
hash_, proof = gp.send(open("document.pdf", "rb").read(), ttl=3600)

# Receive: burn-on-read. One download on Community, more on a paid plan.
data, proof = gp.receive(hash_)

Of gebruik de HTTP-API rechtstreeks, zonder SDK:

curl
# Upload encrypted blob
curl -X POST https://health.paramant.app/v2/inbound \
  -H "X-Api-Key: pgp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"hash":"sha256_of_payload","payload":"base64_5mb_blob","ttl_ms":3600000}'

# Retrieve (Community: one-time, the blob is burned after this)
curl https://health.paramant.app/v2/outbound/abc123... \
  -H "X-Api-Key: pgp_your_key" -o received.bin

Authenticatie

Er zijn twee soorten sleutels, elk met een eigen rol. U kunt ze nooit door elkaar gebruiken.

SleutelPrefixVoor wieDoel
pgp_pgp_EindgebruikersBestanden versturen en ontvangen via de API
plk_plk_RelaybeheerdersLicentiesleutel: maakt meer dan 5 gebruikers mogelijk op een zelf gehoste relay

Geef de pgp_-sleutel mee in de header X-Api-Key. Een sleutel in de queryparameter ?k= weigert de relay met 400, zodat hij niet in een toegangslog belandt:

X-Api-Key: pgp_your_key_here
# bijvoorbeeld:
GET /v2/check-key
X-Api-Key: pgp_your_key_here

SDK

Er zijn twee officiële SDK's. Beide hebben dezelfde encoder voor wire format v1, standaard ML-KEM-768 + ML-DSA-65, en onderhandelen over mogelijkheden via /v2/capabilities. Kies de SDK die bij uw omgeving past: wat ze versturen is byte voor byte gelijk.

PyPI · paramant-sdk npm · paramant-sdk GitHub · broncode sdk-py GitHub · broncode sdk-js

Installeren

# Python (3.10+)
pip install paramant-sdk

# JavaScript (Node 18+)
npm install paramant-sdk

Python-SDK (op basis van klassen)

python
from paramant_sdk import GhostPipe

gp = GhostPipe(
    api_key = "pgp_xxx",
    device  = "device-001",
    sector  = "health",     # routes to health.paramant.app
)
gp.receive_setup()          # register this device's keys first (once per device)

# Send: returns (blob hash, inclusion proof)
hash_, proof = gp.send(open("scan.dcm", "rb").read(), ttl=3600)

# Receive: burn-on-read, returns (plaintext bytes, receipt or None)
data, receipt = gp.receive(hash_)

Dit is paramant-sdk 3.0.0 van PyPI. send en receive geven elk een paar terug. Een apparaat dat iets ontvangt, registreert eerst zijn sleutels met receive_setup(); zonder dat weigert send met "No pubkeys for this device". Het ontvangstbewijs (receipt) is in 3.0.0 meestal None: de SDK leest het alleen uit de oude header X-Paramant-Receipt, die de relay sinds september 2026 standaard niet meer stuurt. Haal het bewijs op met GET /v2/transfers/:receipt_id/receipt (zie docs/api.md).

Toepassingen

PARAMANT heeft vijf sectorrelays, elk afgestemd op de eigen regelgeving. Kies de sector die bij uw toepassing past.

healthZorg: NEN 7510, DICOM, HL7 FHIR

Verstuur MRI-scans, verwijsbrieven en labuitslagen tussen zorgverleners. health.paramant.app is ontworpen voor NEN 7510: versleuteld onderweg, alleen in RAM, onder EU-recht. Elke upload komt als regel in de openbare CT-log; downloaden en bevestigen leggen we vast in de privé-auditketen van uw sleutel (/v2/audit), niet in de CT-log.

python
# Send DICOM scan to specialist
from paramant_sdk import GhostPipe
gp = GhostPipe(api_key="pgp_xxx", device="mri-001", sector="health")
hash_, proof = gp.send(open("scan.dcm","rb").read(), ttl=3600)

legalJuridisch en notariaat: eIDAS, KNB

Verstuur ondertekende aktes en processtukken die na ontvangst cryptografisch gewist zijn. De CT-log legt manipulatiebestendig vast dat het stuk bij de relay is aangeleverd, zonder de inhoud op te slaan. De ophaling en het wissen staan in de privé-auditketen van uw sleutel.

python
# Signed deed: burn-on-read, CT log entry as proof of delivery
from paramant_sdk import GhostPipe
gp = GhostPipe(api_key="pgp_xxx", device="notary-01", sector="legal")
hash_, receipt = gp.send(open("deed.pdf","rb").read())
gp.verify_receipt(receipt)  # ML-DSA-65 signed

iotIndustriële IoT: IEC 62443

PLC- en sensordata doorgeven zonder VPN en zonder dat de OT-omgeving direct aan internet hangt. Werkt als datadiode (met de Python-SDK post-quantum versleuteld): de OT-kant verstuurt alleen naar buiten, de IT-kant ontvangt. De SDK vult elk blok op tot vast 5 MB, dus de grootte van de inhoud blijft binnen een blok verborgen; het aantal blokken van een overdracht met meerdere blokken niet.

python
# PLC telemetry: outbound only, no VPN, no inbound port
from paramant_sdk import GhostPipe
gp = GhostPipe(api_key="pgp_xxx", device="plc-factory-01", sector="iot")
while True:
    gp.send(read_plc_state(), ttl=60)
    time.sleep(15)

financeFinance: NIS2, DORA, ISO 20022

ISO 20022-betaalbestanden doorgeven met een Merkle-bewijs per verzonden bestand (elke upload wordt een regel in de CT-log van de relay) voor DORA-audittrails. finance.paramant.app: EU-recht, geen Amerikaanse Cloud Act, geen bewaring van metadata.

python
# Send ISO 20022 payment file with CT proof for DORA audit
from paramant_sdk import GhostPipe
gp = GhostPipe(api_key="pgp_xxx", device="bank-nl-01", sector="finance")
hash_, receipt = gp.send(open("pacs008.xml","rb").read())
# receipt → tree_size, sha3_root, ML-DSA-65 signature

Alle endpoints

MethodeEndpointOmschrijvingAuth
GET/healthStatus van de node, versie, uptime, editien/a
POST/v2/inboundVersleutelde blob uploaden vanaf een geauthenticeerde client (alleen RAM)Sleutel
POST/v2/anon-inboundVerouderd, vervalt op 31 december 2026 (de datum die de relay meestuurt in de responsheader Sunset). Anoniem uploaden blijft bestaan voor compatibiliteit met sdk-js 3.x. Nieuwe integraties gebruiken /v2/inbound.n/a
POST/v2/sendsMaakt van al geüploade blokken één verzending naar genoemde ontvangers: ieder krijgt een persoonlijke link die één keer werkt. Eén ontvanger per verzending op Community, tot 30 op Firm en Enterprise; meer wordt geweigerd met 403 over_limit. De body bevat de blokhashes, de adressen en per persoon één ingepakte bestandssleutel.Sleutel
POST/v2/pickup/:tokenDe ontvangende kant. Die heeft geen inloggegevens nodig: het token uit de uitnodiging is zelf de toegang. {"action":"code"} mailt een korte code naar dezelfde mailbox; {"code":"123456"} geeft de bytes terug, één keer; {"action":"confirm"} meldt dat het bestand geopend is, en pas dan is het ophalen definitief.n/a
GET/v2/pickup/:tokenDezelfde route, bewaard voor links die zijn gemaakt vóór de overstap naar POST. Een GET die mail verstuurt, wordt door elke linkscanner afgevuurd, dus nieuwe clients gebruiken POST.n/a
GET/v2/outbound/:hashDownloaden en wissen: telt als één lezing van de linkSleutel
GET/v2/status/:hashControleren of een blob beschikbaar is, zonder hem te verbruikenSleutel
POST/v2/ackAflevering bevestigen; vastgelegd in de privé-auditketen van uw sleutel, niet in de CT-logSleutel
GET/v2/monitorLive statistieken: blobs onderweg, ACK-percentageSleutel
POST/v2/ws-ticketEenmalig WebSocket-ticket ophalen, 30 seconden geldigSleutel
GET/v2/streamWebSocket-push: blob_ready-eventsTicket / sleutel
POST/v2/webhookWebhook-URL voor aflevering registrerenSleutel
POST/v2/pubkeyML-KEM- en ECDH-sleutelpaar registrerenSleutel
GET/v2/check-keyGeldigheid en abonnement van een sleutel controlerenn/a
POST/v2/did/registerW3C Decentralized Identity registrerenSleutel
GET/v2/did/:didDID-document opvragenn/a
GET/v2/ct/logCertificate Transparency-log (openbaar)n/a
GET/v2/relaysGeregistreerde relaynodes en hun statusn/a
POST/v2/verifyEen oude .psign (v1/v2) controleren tegen de notarissleutel van deze relayn/a
GET/v2/auditMerkle-auditketen: export als JSON en CSVSleutel
GET/v2/team/devicesApparaten van het team tonenSleutel
POST/v2/team/add-deviceApparaat aan het team toevoegen (Firm of hoger)Sleutel

POST /v2/inbound

Upload een versleutelde, opgevulde blob. Die staat alleen in RAM en wordt nooit naar schijf geschreven. U krijgt een hash terug waarmee u hem ophaalt.

Request body (JSON):
{
  "hash":    "sha256_of_padded_payload",
  "payload": "base64_encoded_5mb_blob",
  "ttl_ms":  3600000,
  "max_views": 1
}

200: { "ok": true, "hash": "...", "ttl_ms": 3600000 }
409: hash already exists
503: relay at capacity (memory limit)

TTL en max_views worden allebei begrensd op het maximum van het abonnement: Community krijgt 1 uur en 1 lezing, Firm 24 uur en tot 10 lezingen, Enterprise 7 dagen en 100 lezingen. Laat u max_views weg, dan wordt de link op elk abonnement bij de eerste lezing gewist. Zo werkt de webapp van Versturen. Het veld hash is de SHA-256 van de opgevulde inhoud, niet van het oorspronkelijke bestand. De SDK berekent dit zelf.

GET /v2/outbound/:hash

Telt als één lezing van de link. Een Community-link heeft één lezing, dus deze aanroep is de enige keer ophalen; een betaalde link kan er meer hebben, tot 10 op Firm. Een lezing telt pas als de hele blob is afgeleverd, dat wil zeggen zodra de relay het laatste byte heeft verstuurd: breekt uw verbinding eerder af, dan staat hij er nog en haalt u hem opnieuw op; de relay levert hem hooguit vijf keer in totaal. Verstuurd betekent hier: aan de verbinding gegeven, niet bij u aangekomen. Het besturingssysteem buffert enkele MB per verbinding, dus een blob tot de standaardgrens van 5 MB is meestal in één keer verstuurd, en een verbinding die na de eerste kilobytes afbreekt, kost de lezing dan al. Wilt u dat een afgebroken download niets kost, gebruik dan de deellink /v2/dl met ?claim=: daar telt de lezing pas na uw eigen bevestiging. Na de laatste afgeleverde lezing wordt de buffer van de blob overschreven met willekeurige bytes en uit het RAM verwijderd. Herstellen is dan niet mogelijk.

GET /v2/outbound/abc123...
X-Api-Key: pgp_xxx

200: binary blob, exactly the bytes that were uploaded (the relay does not pad; the SDK and the live hand-over send 5 MB blocks)
404: burned, expired, or never existed
403: API key mismatch

GET /v2/stream

WebSocket-endpoint voor pushmeldingen van blob_ready in real time. Gebruik een eenmalig ticket, zodat de sleutel niet in de URL staat.

javascript
// Step 1: get a one-time WebSocket ticket (30s TTL)
const { ticket } = await fetch('https://health.paramant.app/v2/ws-ticket', {
  method: 'POST', headers: { 'X-Api-Key': 'pgp_xxx' }
}).then(r => r.json());

// Step 2: connect with ticket (API key never appears in URL)
const ws = new WebSocket(`wss://health.paramant.app/v2/stream?ticket=${ticket}`);
ws.onmessage = e => {
  const msg = JSON.parse(e.data);
  // { type: "blob_ready", hash: "...", size: 5242880, ts: "..." }
};

GET /v2/monitor

{
  "ok": true, "plan": "pro", "blobs_in_flight": 3,
  "stats": { "inbound": 42, "burned": 38, "webhooks_sent": 12 },
  "delivery": { "total": 42, "acked": 38, "success_rate": 0.905 }
}

POST /v2/webhook

{ "device_id": "scanner-01", "url": "https://you.com/hook", "secret": "your-own-secret" }

Response (secret only when you sent none; shown once):
{ "ok": true, "events": ["blob_ready", "blob_downloaded"], "secret": "whsec_..." }

Relay POSTs to your url when a blob for that device arrives (POST /v2/inbound):
{ "event": "blob_ready", "device_id": "scanner-01", "ts": "2026-04-14T...",
  "hash": "...", "size": 5242880, "ttl_ms": 3600000, "sig_valid": true }

And when it is downloaded (via the link or GET /v2/outbound):
{ "event": "blob_downloaded", "device_id": "scanner-01", "ts": "...",
  "hash": "...", "size": 5242880, "via": "link" }   // or "api"

Alleen op Pro en hoger. Elke webhook is ondertekend. Geeft u een secret mee, dan tekent de relay daarmee. Laat u het weg, dan maakt de relay er zelf een (whsec_…) en geeft het één keer terug in het antwoord; bewaar het meteen, want het komt niet opnieuw. Elke aanroep draagt de headers X-Paramant-Event (de eventnaam) en X-Paramant-Sig: de HMAC-SHA256 in hex van de ruwe body, met het secret als sleutel. Daarnaast draagt elke aanroep X-Paramant-Timestamp en X-Paramant-Signature: t=<unix-seconden>,v1=<hex>, de HMAC-SHA256 over <t>.<ruwe body>: daar zit de tijd in de handtekening, dus weiger een aanroep ouder dan vijf minuten en een opgevangen aanroep is niet later opnieuw af te spelen. Reken de handtekening zelf na over de onbewerkte body, vergelijk in constante tijd en weiger callbacks waarvan de handtekening niet klopt. De registratie staat in redis (het geheugen is alleen een cache), dus ze overleeft een herstart en geldt op elke sector; per apparaat blijven de laatste 5 registraties bewaard, een account heeft hoogstens 20 apparaten met een webhook, en een registratie verloopt na 90 dagen tenzij u hem opnieuw doet. De relay probeert één keer (time-out 5 seconden) en doet geen nieuwe poging.

GET /health

{
  "ok": true,
  "version": "3.0.0",
  "sector": "health",
  "edition": "licensed",
  "uptime_s": 3600,
  "license_expires": "2027-01-01"
}

API voor Ondertekenen (/v1)

Gehoste ondertekenrondes vanuit uw eigen systemen: maak een envelope van een pdf en een lijst ondertekenaars, stuur elke ondertekenaar naar een gehoste ondertekenpagina en haal het voltooide .psign-bewijs op. Authenticeer met Authorization: Bearer psk_live_... (sleutels maakt u aan in het ontwikkelaarsdashboard; API-toegang hoort bij Firm).

Endpoints:
POST   /v1/envelopes                Create an envelope; returns id + one sign_url per signer
GET    /v1/envelopes/:id            Envelope status and per-signer progress
GET    /v1/envelopes/:id/receipt    The full .psign proof, once complete
GET    /v1/envelopes/:id/document   The signed PDF, once complete
POST   /v1/envelopes/:id/void       Void an open envelope (owner only)
# Create an envelope. The document travels base64; signers get hosted pages.
curl -X POST https://paramant.app/v1/envelopes \
  -H "Authorization: Bearer psk_live_..." \
  -H "Idempotency-Key: quote-8842-v1" \
  -H "Content-Type: application/json" \
  -d '{
        "document": { "content_base64": "JVBERi0xLjc..." },
        "original_filename": "quote-8842.pdf",
        "signers": [ { "name": "A. Jansen", "email": "a@example.org" } ]
      }'

# On completion, pull the offline-verifiable proof (id from the 201 above).
curl https://paramant.app/v1/envelopes/<id>/receipt \
  -H "Authorization: Bearer psk_live_..." --output quote-8842.psign
201 (shortened):
{
  "id": "Us4rFoLj35sU_4cOlPJcs3eMZlw4xjMp",
  "status": "sent",
  "signers": [
    { "index": 0, "name": "A. Jansen", "status": "pending",
      "sign_url": "https://paramant.app/co-sign?env=...&p=0&t=..." }
  ]
}

Het id is een willekeurige reeks van 20 tot 64 tekens zonder voorvoegsel. Elke ondertekenaar krijgt een eigen sign_url naar de gehoste pagina /co-sign. Een ondertekenaar staat op pending tot hij getekend heeft en daarna op signed, in het antwoord op het aanmaken en in GET /v1/envelopes/:id hetzelfde woord.

Limiet: 50 nieuwe envelopes per sleutel in elk willekeurig uur (een schuivend venster, niet het klokuur), dus ook rond het hele uur nooit meer dan 50 binnen zestig minuten. Een envelope wordt standaard 30 dagen bewaard (in te stellen met ttl_days, maximaal 365). Zolang hij bestaat, bewaart de relay ook de pdf zelf, versleuteld in redis, omdat de ondertekenaars hem moeten kunnen openen en de gestempelde pdf eruit wordt gemaakt. Na het verlopen is hij weg. Webhooks: stel webhook_url in, dan komen envelope.sent, signer.completed, envelope.completed en envelope.voided binnen, ondertekend met HMAC-SHA256 in de header X-Paramant-Sig. Een aflevering die mislukt (geen verbinding, of een 5xx of 429 van uw kant) probeert de relay opnieuw na 2 en na 10 seconden, drie pogingen in totaal, met dezelfde body en dezelfde X-Paramant-Delivery, zodat u een herhaling op die id herkent; X-Paramant-Attempt zegt welke poging het is. De events van één envelope gaan in volgorde, één voor één, en elke body draagt een oplopend seq (envelope.sent 1, signer.completed 1 plus het aantal handtekeningen, envelope.completed en envelope.voided het aantal ondertekenaars plus 2). Na drie mislukte pogingen geeft de relay het op, dus controleer de status ook zelf met GET /v1/envelopes/:id. Bewijzen zijn offline te controleren tegen de openbare CT-log; zie /verify.

Veilig opnieuw proberen: stuur bij POST /v1/envelopes een header Idempotency-Key mee (8 tot 128 tekens uit A-Z a-z 0-9 _ . : -). Komt dezelfde sleutel binnen 24 uur opnieuw van dezelfde API-sleutel, dan krijgt u het eerste 201-antwoord terug met Idempotent-Replay: true, zonder tweede envelope en zonder quotum te verbruiken. Alleen een geslaagde 201 wordt bewaard. De relay controleert de body vóór het uurquotum: 400 ambiguous_document (zowel content_base64 als url), 400 invalid_binding_mode (niet email of open), 400 invalid_signer_email (bij email-binding heeft elke ondertekenaar een geldig adres nodig; signer_index noemt de eerste foute) 400 invalid_metadata (metadata is geen object) en 400 invalid_idempotency_key kosten dus geen aanmaak. Verder: 400 missing_signers, 400 missing_document (geen content_base64 en geen url), 400 empty_document (lege content_base64), 400 too_many_signers (meer namen dan uw abonnement op één document toelaat; max_signers zegt hoeveel), 413 document_too_large, 422 not_a_pdf en 422 document_unfetchable. Een 429 rate_limited draagt Retry-After met de seconden tot de oudste aanmaak van het afgelopen uur uit het venster valt. Is het maandquotum handtekeningen van uw abonnement op, dan antwoordt de relay 402 monthly_sign_quota_reached met plan, limit, used, reset_date en Retry-After: 86400; er wordt dan niets aangemaakt. Een fout op /v1 heeft altijd de vorm {"error": "<code>", "message": "<zin>"}.

GET /v2/user/history

De geschiedenis van verzendingen en envelopes van uw eigen account: een alleen-lezen weergave van de Merkle-auditketen per sleutel. U krijgt alleen kenmerken, status en tijden terug, nooit inhoud, een downloadtoken of een sleutel. Authenticeer met uw pgp_-sleutel in de header X-Api-Key.

VeldWaarde
AuthX-Api-Key: pgp_...
AbonnementFirm of hoger op Versturen of Ondertekenen. Community → 403.
Query?limit= (standaard 100, maximaal 1000)
GET /v2/user/history?limit=100
X-Api-Key: pgp_xxx

# 200 OK
{
  "ok": true,
  "count": 2,
  "entries": [
    { "id": "9f2c1a...", "status": "downloaded_burned", "time": "2026-07-19T14:22:03.114Z", "recipient_hash": "dev-88a1", "bytes": 5242880 },
    { "id": "b7e0d4...", "status": "sent",               "time": "2026-07-19T14:05:41.882Z", "recipient_hash": "dev-2f10", "bytes": 5242880 }
  ]
}

De statussen komen uit de auditketen: sent (inbound), aborted (inbound afgebroken), downloaded (outbound bekeken), downloaded_burned (outbound gewist). Nieuwste eerst, daarna afgekapt op limit.

# 401 no / invalid key
{ "error": "API key required" }

# 403 plan too low
{ "error": "tier_upgrade_required", "feature": "history",
  "message": "Send history and link management require a Firm plan or higher." }

GET /v2/parasign/audit-export

Dit hoort bij de betaalde abonnementen van Ondertekenen en is bedoeld voor compliance. Een auditor krijgt het manipulatiebestendige ondertekenspoor van het account, de ondertekende tree head (STH) uit de Certificate Transparency-log die het verankert, en de volledige .psign-bewijzen van elke voltooide envelope, als CSV of JSON. Er verlaat geen document de relay; de export bevat alleen hashes en handtekeningen.

VeldWaarde
AuthX-Api-Key: pgp_...
AbonnementBusiness of hoger (het recht audit_export). Firm en lager → 403.
Query?format=csv voor CSV (standaard JSON); ?limit= (standaard 1000, maximaal 10000)
# JSON (default)
curl https://paramant.app/v2/parasign/audit-export \
  -H "X-Api-Key: pgp_xxx"

# CSV, saved to a file
curl "https://paramant.app/v2/parasign/audit-export?format=csv" \
  -H "X-Api-Key: pgp_xxx" --output parasign_audit.csv
# 200 OK (JSON)
{
  "ok": true,
  "type": "parasign-audit-export",
  "generated_at": "2026-07-19T14:30:00.000Z",
  "chain_valid": true,
  "ct_head": { "tree_size": 10432, "tree_hash": "sha3-256:...", "ts": "2026-07-19T14:29:00Z" },
  "count": 2,
  "entries": [
    { "time": "2026-07-19T14:22:03Z", "event": "sign", "doc_hash": "b3f1...", "bytes": 82113, "device": "notary-01", "chain_hash": "a19c..." }
  ],
  "envelope_count": 1,
  "envelopes": [
    { "envelope_id": "Us4rFoLj35sU_4cOlPJcs3eMZlw4xjMp", "status": "completed", "psign": { "...": "full multi-signer .psign proof" } }
  ]
}

De CSV-export heeft één kopregel en één regel per auditgebeurtenis: time,event,doc_hash,bytes,device,chain_hash (de .psign-bewijzen van envelopes staan alleen in de JSON-export). chain_valid: false wijst op manipulatie: een auditketen die de Merkle-hercontrole niet doorstond. Envelopestatussen zijn completed (volledig .psign), in_progress, sent, void of expired_or_gone (geen bewijs).

# 403 plan too low
{ "error": "tier_upgrade_required", "feature": "audit_export",
  "message": "The ParaSign audit export requires a Business plan or higher." }

Betaal-API: POST /v2/billing/checkout

Start een Mollie-betaling voor een Paramant-abonnement. De aanroeper geeft nooit zelf de prijs op: de server zoekt die op in de prijscatalogus aan de hand van (product, plan, interval), en de webhook controleert het werkelijk betaalde bedrag voordat er iets wordt toegekend. Authenticeer met uw pgp_-sleutel.

VeldWaarde
AuthX-Api-Key: pgp_... (een geldige sleutel). Geen sleutel → 401.
productfirm, of parasign voor Business
planFirm: product firm, plan firm. Dat geeft de betaalde limieten van Ondertekenen en Versturen samen. Ondertekenen: business.
intervalmonthly of yearly

De losse abonnementen parasign/pro en parasend/pro worden niet meer verkocht. Ze blijven in de catalogus staan, zodat een lopende verlenging en een bestaand Mollie-abonnement blijven werken.

curl -X POST https://paramant.app/v2/billing/checkout \
  -H "X-Api-Key: pgp_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "product": "parasign", "plan": "business", "interval": "monthly" }'

# 200 OK: redirect the user to checkout_url to pay
{ "ok": true, "payment_id": "tr_XXXX", "checkout_url": "https://www.mollie.com/checkout/...", "mode": "live" }

Na een geslaagde betaling stuurt Mollie de gebruiker terug naar /dashboard?billing=return en roept de webhook hieronder aan. Het account krijgt zijn rechten via de webhook, nooit via de doorverwijzing.

# 400 unknown product / plan / interval, or bad JSON
{ "error": "unknown_plan" }        # also: unknown_product, unknown_interval, bad_json
# 400 a plan the site does not sell (the separate Pro plans)
{ "error": "not_on_sale" }
# 409 another plan is running on that product; changing plans goes by mail
{ "error": "other_plan_running", "running": "business", "paid_until": "...", "message": "..." }
# 401 no / invalid key
{ "error": "unauthorized" }
# 502 the payment provider call failed
{ "error": "checkout_failed" }

POST /v2/billing/webhook

Wordt aangeroepen door Mollie, niet door uw integratie. Mollie stuurt met POST een formulierveld id=tr_.... De relay haalt die betaling opnieuw op bij Mollie (en vertrouwt alleen de aanbieder, nooit de body van het verzoek), controleert het bedrag tegen de catalogus en kent het recht idempotent toe. Zodra een event verwerkt is, antwoordt hij altijd 200, zodat Mollie stopt met opnieuw proberen; bij een tijdelijke fout bij het ophalen komt er 503 terug, zodat Mollie het opnieuw probeert. Geen API-sleutel: de echtheid volgt uit het opnieuw ophalen van de betaling op id.

# Mollie -> relay (application/x-www-form-urlencoded)
POST /v2/billing/webhook
id=tr_XXXX

# 200 handled          { "ok": true }
# 400 malformed payment id  { "error": "bad_payment_id" }
# 503 provider fetch failed (Mollie will retry)  { "error": "fetch_failed" }

Zelf hosten: Docker Compose

Community Edition is altijd gratis voor maximaal 5 gebruikers. Eén docker compose up start 6 containers: vijf sectorrelays en een beheerpaneel.

Vereisten

Vereisten: Ubuntu 22.04+ / Debian 12+ · Docker 24+ · minimaal 1 GB RAM · swap uitgeschakeld · een domein met wildcard-DNS

Waar u begint

Deze links staan niet meer in het menu, alleen nog hier. Installeren met één regel: install.sh voor een server, install-pi.sh voor een Raspberry Pi. Images: Docker Hub. Broncode en tags: GitHub, Releases. Clientbibliotheken: SDK op PyPI, SDK op npm.

bash
# 1. Clone
git clone https://github.com/Apolloccrypt/paramant-relay
cd paramant-relay

# 2. Configure
cp .env.example .env
nano .env   # vul de verplichte geheimen in:
#   ADMIN_TOKEN, REDIS_PASSWORD, INTERNAL_AUTH_TOKEN: openssl rand -hex 32
#   RELAY_REDIS_URL=redis://:<REDIS_PASSWORD>@redis:6379
#   PARAMANT_TOTP_MASTER_KEY: openssl rand -base64 32
#   PARASIGN_PUBLIC_ORIGIN, RELAY_SELF_URL_<SECTOR>: uw eigen publieke adres
#   (install.sh schrijft deze allemaal voor u)

# 3. Launch
docker compose up -d

# 4. Verify
curl http://localhost:3000/health
# → {"ok":true,"version":"3.1.3","sector":"relay"}

Poorten per container

ContainerHostpoortSectorAanbevolen domein
relay-main127.0.0.1:3000relayrelay.your-domain.com
relay-health127.0.0.1:3001healthhealth.your-domain.com
relay-finance127.0.0.1:3002financefinance.your-domain.com
relay-legal127.0.0.1:3003legallegal.your-domain.com
relay-iot127.0.0.1:3004iotiot.your-domain.com
admin127.0.0.1:4200n/ayour-domain.com/admin/

Belangrijkste .env-variabelen

VariabeleVerplichtOmschrijving
ADMIN_TOKENJaWachtwoord van het beheerpaneel (minimaal 32 tekens, hex)
REDIS_PASSWORDJaWachtwoord van de redis-container; leeg start redis niet
RELAY_REDIS_URLJaredis://:<REDIS_PASSWORD>@redis:6379
PARAMANT_TOTP_MASTER_KEYJaVersleutelt de TOTP-geheimen van gebruikers (32 bytes, base64); verander hem nooit op een draaiende installatie
INTERNAL_AUTH_TOKENJaTweede slot op de interne routes van de relays (beheer naar relay)
PARASIGN_PUBLIC_ORIGINNeeUw eigen publieke site; de betaling keert hierheen terug in plaats van naar paramant.app
RELAY_SELF_URL_<SECTOR>NeePer relay (MAIN, HEALTH, FINANCE, LEGAL, IOT) zijn eigen publieke adres; zonder dit noemen ondertekende tree heads en bewijzen een paramant.app-host
TOTP_SECRETAanbevolenBase32 TOTP-geheim: zet MFA aan bij het inloggen als beheerder
PLK_KEYNeeLicentiesleutel: maakt meer dan 5 gebruikers mogelijk (Community = gratis, maximaal 5)
RESEND_API_KEYNeeSleutel van de e-mailaanbieder (voor meldingen bij het afleveren van sleutels)
LOG_LEVELNeeinfo (standaard) · debug · warn · error
RAM_LIMIT_MBNeeGeheugenlimiet voor blobopslag per container (standaard 1024)

nginx en TLS

Gebruik nginx op het systeem om TLS af te handelen vóór de Docker-containers. Certbot vernieuwt de certificaten automatisch.

bash
# Install nginx + certbot
apt install -y nginx python3-certbot-nginx

# Obtain wildcard TLS certificates
certbot certonly --nginx -d your-domain.com \
  -d relay.your-domain.com \
  -d health.your-domain.com \
  -d finance.your-domain.com \
  -d legal.your-domain.com \
  -d iot.your-domain.com

Voeg per subdomein een nginx-serverblok toe dat doorstuurt naar de bijbehorende containerpoort:

nginx
server {
    listen 443 ssl;
    server_name health.your-domain.com;
    ssl_certificate     /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    location / {
        proxy_pass         http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection 'upgrade';
        proxy_set_header   Host $host;
        proxy_read_timeout 3600s;
    }
}
# Repeat for relay (:3000), finance (:3002), legal (:3003), iot (:3004)

Eerste gebruiker

bash
# Create your first API key
python3 deploy/paramant-admin.py add \
  --label "alice" \
  --plan  pro \
  --email alice@example.com

# Push to all relay containers
python3 deploy/paramant-admin.py sync

# Admin panel (requires ADMIN_TOKEN + TOTP if configured)
open https://your-domain.com/admin/

Community Edition heeft een harde grens van 5 gebruikers: de 6e add geeft HTTP 402. Zet een PLK_KEY in .env voor een onbeperkt aantal gebruikers.

Nieuw in 3.0.0

Begeleide installatie: een webwizard op /setup vervangt bij de eerste start de handmatige beheerscripts voor nieuwe beheerders. U maakt het admin-token aan, registreert TOTP en maakt de eerste sleutel, allemaal vanuit de browser. De wizard werkt alleen bij de eerste start (nog geen sleutels) of met de expliciete vlag SETUP_MODE, en voert grondiger controles uit via /v2/health/deep en DNS voordat hij meldt dat alles werkt (ontwerp: ADR R005). Draait de relay eenmaal, dan toont /admin/settings de configuratie in beeld, zodat u .env niet meer met de hand bewerkt. /admin/cli geeft beheerders een terminal in de browser voor foutopsporing zonder SSH.

Installatiecode

De installatie afronden is niet anoniem: POST /v2/setup/apply vraagt het eenmalige installatietoken in de header X-Setup-Token (of uw ADMIN_TOKEN in X-Admin-Token), anders antwoordt hij 401 setup_token_required. Bij de start schrijft de relay een vers token (pst_...) in het bestand setup-token naast users.json (modus 600); het log noemt alleen het bestand, nooit het token. Plak het in het veld Installatiecode van de wizard. Na de installatie wordt het verwijderd en werkt het niet meer. U vindt het met docker compose exec relay-main cat /data/setup-token.

Nieuw in 3.0.0

Direct bruikbaar: met SERVE_FRONTEND=true serveert één relay zijn eigen interface via een alleen-lezen statische handler, zodat een nieuwe zelf gehoste installatie geen aparte webserver nodig heeft (optioneel, standaard uit; ADR R011). Met CRYPTO_MODE kiest u welke algoritmen de relay meldt op /v2/capabilities: core (standaard: ML-KEM-768 + ML-DSA-65) of extended voor de volledige optionele set (ADR R006).

Upgraden

bash
# Geïnstalleerd met install.sh: haalt de nieuwste v*-releasetag op
# (of $PARAMANT_VERSION), bouwt opnieuw en herstart
paramant upgrade

# Met de hand: de clone staat vast op een tag, git pull verplaatst hem niet
cd /opt/paramant
git fetch --depth 1 origin tag v3.1.3
git checkout v3.1.3
docker compose up -d --build
# Named volumes (users.json, CT log, relay identity) are preserved
Belangrijk

Let op: gebruikersgegevens en de CT-log blijven bij upgrades bewaard in benoemde Docker-volumes. Het identiteitssleutelpaar van de relay (ML-DSA-65) wordt eenmalig aangemaakt bij de eerste start en opgeslagen in /data/relay-identity.json. Verwijder dit volume niet, anders verliest de relay zijn geregistreerde identiteit. Sinds v3.0.0 komt het ondertekenen met ML-DSA-65 achter deze identiteit uit paramant-core, de Rust-cryptobibliotheek van de relay die klaar is voor audit (gebruikt via de NAPI-binding @paramant/core). Die zit in de Docker-image, dus een extra installatiestap is niet nodig.

Beheerscripts

De beheerscripts zitten in de repository van de relay. Na het clonen van paramant-relay staat het beheerprogramma voor de server onder deploy/: voer python3 deploy/paramant-admin.py [command] direct uit. Hulpscripts voor beheerders (sleutels toevoegen of intrekken, installatie bij de eerste start) staan onder scripts/. Een installatiepakket voor het hele systeem is er op dit moment niet: de SDK-pakketten op PyPI (paramant-sdk) en npm (paramant-sdk) zijn de officiële clientintegraties.

Binnenkort

Add-ons: er is een architectuur voor add-ons gespecificeerd, voor integraties net buiten de kern van de relay: opslagspiegels, koppelingen voor meldingen, SIEM-exporters, OIDC/SAML SSO. Elk in een eigen container, met mogelijkheden die in een manifest staan, zoals bij HomeAssistant. Ze werken alleen met versleutelde gegevens en metadata, nooit met leesbare inhoud of sleutels. Specificatie: ADR R007, concept.

CLI-referentie

Er is geen installeerbare CLI: pip install paramant-sdk en npm install paramant-sdk leveren alleen de bibliotheek, geen commando's. Wat er wel is, zijn scripts in de repository van de relay, die u vanuit een clone draait:

ScriptOmschrijving
scripts/paramant-sender.pyEen bestand, stdin of tekst versleutelen en uploaden; met --watch DIR een map in de gaten houden
scripts/paramant-receiver.pyOp hash ophalen en ontsleutelen; met --listen blijven luisteren
scripts/paramant-verify-sthDe ondertekende boomkop (STH) van een relay controleren
scripts/paramant-verify-peersDe gespiegelde boomkoppen van andere relays controleren
scripts/paramant-scan.shRelaynodes vinden via het register
deploy/paramant-admin.pyGebruikers en sleutels op uw eigen relay beheren

De twee Python-scripts nemen met --relay een gehoste sector (health, legal, finance, iot) of het https-adres van uw eigen relay, bijvoorbeeld --relay https://relay.example.com. Ze versleutelen met een eigen transfergeheim (--secret of PARAMANT_TRANSFER_SECRET), nooit met de API-sleutel.

# Example: send a file (the transfer secret is printed once; give it to the receiver)
python3 scripts/paramant-sender.py --key pgp_xxx --relay health --file scan.dcm

# Example: receive by hash with that secret
python3 scripts/paramant-receiver.py --key pgp_xxx --relay health --hash <hash> --secret <secret>

Cryptografie

Op elke route op deze pagina, op één na, gebeurt het versleutelen aan de clientkant. De relay ziet nooit leesbare inhoud en heeft nooit een privésleutel, met één uitzondering: de gehoste ondertekenronde op de /v1-API. Het curl-voorbeeld hierboven uploadt een pdf als content_base64, en dan leest de relay een document omdat dat verzoek erom vroeg.

AlgoritmeRolStandaard
ML-KEM-768Post-quantum sleutelinkapselingNIST FIPS 203
ECDH P-256Klassieke sleuteluitwisseling (hybride, extra verdedigingslaag)NIST SP 800-56A
AES-256-GCMSymmetrische geauthenticeerde versleutelingNIST FIPS 197
HKDF-SHA256Sleutelafleiding uit de uitvoer van de hybride KEMRFC 5869
SHA3-256Merkle-hashes van de CT-log · DID-hashesNIST FIPS 202
ML-DSA-65Identiteitshandtekeningen van de relay (authenticatie tussen relays)NIST FIPS 204
Argon2idWachtwoord op een overdracht (hash, optionele module)RFC 9106

Versleuteling in de browser draait als Rust/WASM, gecompileerd uit crypto-wasm/. De WASM-binary wordt tijdens het draaien met SHA-256 gecontroleerd, vóór het eerste gebruik. De JS-code van de relay kan op geen enkel moment bij sleutels om te ontsleutelen.

De twee helften draaien verschillende builds van dezelfde Rust-cryptografie. Sleutelinkapseling met ML-KEM-768 blijft aan de clientkant (WASM in de browser of de SDK): de relay wisselt nooit sleutels uit en heeft nooit een privésleutel. ML-DSA-65 draait aan de serverkant, voor het identiteitssleutelpaar van de relay, ondertekende ontvangstbewijzen en ondertekende tree heads (STH), en om handtekeningen van apparaten en ontvangstbewijzen te controleren. Sinds de M5b-migratie (27 mei 2026) komt die ML-DSA-65 aan de serverkant uit paramant-core: een losse Rust-cryptobibliotheek, klaar voor audit (BUSL-1.1), die de relay gebruikt via de NAPI-binding @paramant/core.

Burn-on-read: wissen na ophalen

Zodra GET /v2/outbound/:hash de laatste byte heeft afgeleverd, overschrijft de relay de buffer van de blob met cryptografisch willekeurige bytes en verwijdert hij de vermelding uit het geheugen. Breekt de ontvanger de download af voordat de relay de laatste byte aan de verbinding gaf, dan blijft de blob staan en levert de volgende poging hem opnieuw, helemaal. Bij een blob tot een paar MB is die laatste byte door de buffers van het besturingssysteem meestal meteen weg. Versleutelde inhoud wordt tijdens de overdracht nooit naar schijf geschreven.

De CT-log slaat cryptografische hashes op schijf op (Merkle-leafhashes, tree-hashes, apparaathashes), nooit inhoud. Zelfs een volledige schijfkopie van de relayserver laat geen bestandsinhoud zien.

Wissen in twee stappen: eerst wordt de blob ter plekke met nullen overschreven, daarna wordt de Map-vermelding verwijderd. Zo is er geen kort moment waarop een gedeeltelijke lezing mogelijk is.

Vaste opvulling tot 5 MB (SDK en live overdracht)

De opvulling gebeurt bij de afzender, niet op de relay: de relay bewaart en levert precies de bytes die hij krijgt. De SDK's (standaard) en de live overdracht in de webapp van Versturen vullen elk blok vóór het uploaden op tot precies 5.242.880 bytes (5 MiB) met cryptografisch willekeurige bytes. Een eenmalige link uit de webapp en een verzending naar ontvangers op naam worden niet opgevuld; die blobs hebben hun echte versleutelde grootte. Bij een opgevuld blok geldt: de blokgrootte zegt een waarnemer niets over het bestand. Maar een overdracht groter dan één blok gaat als meerdere blokken, en total_chunks reist onversleuteld mee. Het aantal blokken is dus zichtbaar, en de bestandsgrootte is op een orde van grootte te schatten. Dat is bevinding 5 van de audit van april 2026, een bewust geaccepteerde afweging.

Certificate Transparency-log

Elke overdrachtshash en elke apparaatregistratie wordt toegevoegd aan een openbare Merkle-boom. De root-hash verandert bij elke toevoeging en is door iedereen te controleren. Iedereen kan aantonen dat een overdracht plaatsvond, zonder te weten wat er is overgedragen.

GET https://health.paramant.app/v2/ct/log?limit=100&from=0

{
  "root": "cf7436a9efa4ee9a...",
  "entries": [
    { "index": 56, "type": "transfer", "leaf_hash": "ecb779...", "tree_hash": "cf7436...",
      "ts": "2026-04-14T19:00:00.000Z" }
  ]
}

Live CT-log: paramant.app/ct-log →

W3C Decentralized Identity

Apparaten kunnen een W3C DID registreren op basis van ML-DSA-65- en ECDH-sleutels. Daarna worden verzoeken ondertekend, zonder API-sleutel in de header.

POST /v2/did/register
{ "device_id": "mri-001", "ecdh_pub": "base64...", "dsa_pub": "base64..." }

{ "did": "did:paramant:abc123...", "ct_index": 42 }

# Authenticate with DID:
X-DID: did:paramant:abc123...
X-DID-Signature: base64_request_signed_with_dsa_key

Teambeheer

# Create an API key (admin script)
python3 deploy/paramant-admin.py add \
  --label "scanner-room-3" --plan pro --email admin@hospital.nl

# Sync to all relay containers
python3 deploy/paramant-admin.py sync

# Revoke a key immediately
python3 deploy/paramant-admin.py revoke --key pgp_xxxxx
python3 deploy/paramant-admin.py sync

Sleutels wijzigen kan zonder onderbreking: bij een sync laadt de relay users.json opnieuw in zonder herstart. Ingetrokken sleutels krijgen binnen enkele seconden WebSocket-sluitcode 4401.

Bewaartermijnen

AbonnementStandaard-TTLMaximale TTLMaximale bestandsgrootte
Community (pgp_)1 uur1 uur500 MB
Firm1 uur24 uur500 MB
Enterprise1 uur7 dagen500 MB, of de limiet die voor uw eigen relay is ingesteld

De abonnementen verschillen niet in bestandsgrootte, dus het plafond is overal gelijk. Twee andere getallen worden hier vaak mee verward. Een blob van de SDK op POST /v2/inbound is een vast blok van 5 MB, de opvulgrootte onderweg, dus een bestand groter dan één blok gaat als meerdere blokken. En een eenmalige link uit de webapp van Versturen is één blok onder één token, dus die ene manier van versturen stopt bij 5 MB per bestand. De live overdracht in dezelfde webapp knipt het bestand in stukken en haalt wel de 500 MB uit deze tabel.

Relay-sectoren

Alle sectornodes draaien dezelfde Ghost Pipe-relaysoftware. Sectoren zijn routeringsdomeinen: ze houden verkeer gescheiden en hebben compliance-documentatie per sector. De cryptografie en de API zijn overal gelijk.

SectorURLBelangrijkste gebruikCompliance
healthhealth.paramant.appDICOM, patiëntdossiers, vitale functiesNEN 7510, AVG
legallegal.paramant.appContracten, notariaat, bewijsstukkenAVG, eIDAS
financefinance.paramant.appISO 20022, compliancegegevensNIS2, DORA
iotiot.paramant.appSCADA, PLC's, sensorstromenIEC 62443

Beveiliging: dreigingsmodel

Het ontwerp gaat ervan uit dat de relay niet te vertrouwen is. De beveiliging hangt niet af van vertrouwen in de beheerder.

Wat een gecompromitteerde relay kanWat hij niet kan
Dienst weigeren of vertragenBestandsinhoud lezen (geen sleutel om te ontsleutelen)
Tijdstip en frequentie van overdrachten zienEen geregistreerde publieke ML-KEM-sleutel vervangen (de CT-log voorkomt terugdraaien)
Zien dat er een blob binnenkwam (inhoud onleesbaar; van de SDK en de live overdracht vast 5 MB, van een eenmalige link of een verzending op naam de echte versleutelde grootte)Handtekeningen van de relay vervalsen (ML-DSA-65-sleutel per relay, ondertekend)
Een bepaalde apparaathash blokkerenOpgeslagen versleutelde gegevens ontsleutelen

Het dreigingsmodel gaat uit van aanvallers op netwerkniveau, gecompromitteerde relaybeheerders en aanvallers met een quantumcomputer. Bij de live overdracht in de webapp is de versleuteling hybride (ML-KEM-768 + ECDH P-256), zodat het breken van één van beide niet genoeg is: beide moeten tegelijk gebroken worden. De Python-SDK gebruikt ML-KEM-768. Een eenmalige link en een verzending op naam gebruiken AES-256-GCM zonder ML-KEM.

De kern

De kern: bij de live overdracht, de SDK en een eenmalige link bewaart de relay versleutelde blobs die hij niet kan ontsleutelen, en Merkle-hashes die hij niet kan vervalsen. Twee uitzonderingen. Bij een verzending naar ontvangers op naam krijgt de relay in hetzelfde verzoek de ingepakte sleutel en het token dat hem uitpakt, en dat token gaat per e-mail naar de ontvanger; dat is dus niet zero-knowledge. En de ParaSign-API /v1 bewaart de pdf zelf, versleuteld met een sleutel van de relay, zolang de envelope bestaat.

Rechtsgebied

De beheerde relay-infrastructuur draait uitsluitend bij Hetzner in Neurenberg, Duitsland. EU-recht en de AVG zijn van toepassing. De Amerikaanse Cloud Act geldt niet: Hetzner is een Duits bedrijf zonder Amerikaans moederbedrijf. Bestanden en documenten gaan niet buiten de EU. De uitzondering is e-mail: het e-mailadres en de link gaan via Resend in de Verenigde Staten. Bij een verzending naar ontvangers op naam opent die link het bestand, dus dan reist de sleutel tot het bestand via Resend. Elke partij met haar status: /partners.

Beveiligingsaudits

DatumAuditorBevindingenStatus
Apr 2026R. Zwarts (verificatie)14 in totaal (1H · 8M · 5L)Alles opgelost: e6f216d
Apr 2026R. Zwarts (onafhankelijk)6 in totaal (3H · 3M)Alles opgelost: 0db3ef0
Apr 2026Ryan Williams · Smart Cyber Solutions4C · 5H · 6M · 5LOpgelost in 0db3ef0, op drie na: #4 (kritiek, bestandsnaam leesbaar in het RAM van de relay), #6 (hoog, het wissen van sleutels in de Python-scripts werkt alleen op CPython en waarschuwt nog niet) en #14 (CT-boom niet volgens RFC 6962) staan nog open, zie SECURITY.md

Alle bevindingen staan openbaar in SECURITY.md. Een kwetsbaarheid melden: privacy@paramant.app.

Compliance: NIS2 / DORA

NIS2 (EU 2022/2555) geldt voor aanbieders van essentiële diensten in de EU. PARAMANT helpt bij NIS2 met:

  • Post-quantum versleuteling bij de live overdracht en de SDK (cryptografie die ook later standhoudt)
  • Merkle CT-log: manipulatiebestendig auditspoor voor elke overdracht
  • Infrastructuur alleen in de EU (Hetzner DE): bestanden blijven in het EU-rechtsgebied; e-mail loopt via Resend in de VS, en bij een verzending op naam zit in die mail de link met het token dat het bestand opent
  • Burn-on-read: zo min mogelijk bewaarde gegevens
  • Sleutels intrekken zonder onderbreking: direct ingrijpen bij een incident

DORA (EU 2022/2554) geldt voor financiële instellingen. De sector finance.paramant.app is ontworpen met DORA in gedachten: Merkle-bewijs per verzonden bestand, geen Amerikaanse Cloud Act, geschikt voor ISO 20022.

NEN 7510 (zorg)

NEN 7510 is de Nederlandse norm voor informatiebeveiliging in de zorg. De sector health.paramant.app is ontworpen voor NEN 7510:

  • Versleuteld onderweg en in opslag (technisch: alleen RAM, nooit schijf)
  • Uploads in de openbare CT-log; elke download en bevestiging in de privé-auditketen van uw sleutel (/v2/audit)
  • Registratie van apparaatidentiteit (DID): toegang per apparaat te herleiden
  • Toegang op basis van sleutels: API-sleutels per gebruiker, in te trekken
  • Hetzner DE: EU-recht en AVG, geschikt voor het doorgeven van patiëntgegevens

IEC 62443 (industrie / OT)

IEC 62443 is de internationale norm voor de beveiliging van operationele technologie. De sector iot.paramant.app ondersteunt toepassingen onder IEC 62443:

  • Werkt als quantumveilige datadiode: PLC's en sensoren versturen data zonder inkomende poorten te openen
  • Geen VPN, geen certificaten aan de OT-kant: werkt met een Raspberry Pi en elk Linux-apparaat
  • Vaste blokken van 5 MB (de SDK vult op): een heartbeat van één blok en inhoud van één blok zien er onderweg hetzelfde uit; bij een overdracht van meerdere blokken blijft het aantal blokken zichtbaar
  • Netwerksegmentatie: de OT-kant maakt alleen uitgaande verbindingen; de IT-kant ontvangt alleen inkomend
  • Apparaathandtekeningen met ML-DSA-65: cryptografische authenticatie van apparaten

Scripts voor beheerders

Wie zelf host, krijgt een set hulpprogramma's voor de opdrachtregel onder scripts/ in de repo paramant-relay. Log met SSH in op uw relayserver en voer ze uit vanuit de hoofdmap van de repo. Voor de meeste is er een variant in de browser op /admin/cli (inloggen als beheerder vereist).

  • Sleutels. scripts/paramant-key-add.sh: een API-sleutel aanmaken.
  • Ondertekenen. scripts/paramant-sign: controleert een .psign-envelope (verify --psign ... --document ...). Document en handtekening controleert hij lokaal; de notarishandtekening vraagt hij aan de relay (POST /v2/verify), dus daarvoor is een verbinding nodig. Het commando sign werkt niet meer: de relay heeft /v2/sign uitgezet (410). Ondertekenen gaat in de browser op /sign.
  • Veilig uitrollen. scripts/post-deploy-verify.sh: snelle controle na het uitrollen (exitcode 2 bij een kritieke fout); scripts/rollback-3.0.0.sh: terug naar de vorige gebouwde relay-image.
  • Reservekopieën. scripts/backup-users-json.sh: met age versleutelde reservekopie van de sleutelmetadata van de relay (staat al in cron, dagelijks om 03:15; alleen ontsleutelen in tmpfs).
bash
# on the relay host, from the repo root
./scripts/paramant-key-add.sh "customer-acme" pro ops@acme.example
./scripts/post-deploy-verify.sh https://paramant.app

Veelgestelde vragen

Kan de relaybeheerder mijn bestanden lezen?
Nee. Het versleutelen gebeurt in de browser of de SDK, voordat het bestand uw apparaat verlaat. De relay ontvangt versleutelde gegevens die hij niet kan ontsleutelen. Zelfs wie volledige rootrechten op de relayserver krijgt, vindt alleen versleutelde blobs en Merkle-hashes.
Wat gebeurt er nadat het bestand is gedownload?
De buffer van de blob wordt direct overschreven met willekeurige bytes en uit het geheugen verwijderd. Er is geen herstel, geen reservekopie en geen tweede download. De CT-log bevat alleen de hash van het uploaden; het ophalen en wissen staan in de privé-auditketen van uw sleutel. Geen van beide bevat de inhoud van het bestand.
Is Community Edition echt altijd gratis?
Ja. Tot 5 gebruikers, zonder licentiesleutel en zonder tijdslimiet. De 6e gebruiker geeft HTTP 402: zet een PLK_KEY in .env voor een onbeperkt aantal gebruikers. De broncode is beschikbaar onder BUSL-1.1.
Wat is het verschil tussen pgp_- en plk_-sleutels?
pgp_-sleutels zijn voor eindgebruikers: ze authenticeren API-aanroepen om bestanden te versturen en te ontvangen. plk_-sleutels zijn licentiesleutels voor relaybeheerders: ze staan in .env, niet in API-aanroepen, en maken een onbeperkt aantal gebruikers mogelijk. Het zijn nooit dezelfde sleutels en ze zijn niet uitwisselbaar.
Hoe beschermt de opvulling tot 5 MB mij?
De SDK en de live overdracht vullen elk blok op tot dezelfde 5 MiB (een eenmalige link en een verzending op naam niet), dus een passieve waarnemer ziet geen verschil tussen een heartbeat van 1 KB en een DICOM-scan van 4,9 MB: beide zijn één blok. De blokgrootte zegt een waarnemer niets over het bestand. Maar een overdracht groter dan één blok gaat als meerdere blokken, en total_chunks reist onversleuteld mee. Het aantal blokken is dus zichtbaar, en de bestandsgrootte is op een orde van grootte te schatten. Dat is bevinding 5 van de audit van april 2026, een bewust geaccepteerde afweging.
Kan ik dit op een Raspberry Pi draaien?
Ja. De relay draait op arm64: Raspberry Pi 3B+, 4 en 5 worden ondersteund. Het script curl -fsSL https://paramant.app/install-pi.sh | bash herkent uw Pi-model, installeert Docker, schakelt swap uit en start de relay. Minimaal 512 MB RAM voor een relay met één sector.
Is de broncode te controleren?
Ja. De volledige broncode van de relay staat op github.com/Apolloccrypt/paramant-relay onder BUSL-1.1. In april 2026 zijn drie onafhankelijke beveiligingsaudits uitgevoerd; alle bevindingen staan openbaar in SECURITY.md. De checksum van de relaybinary wordt bij het opstarten gecontroleerd en vastgelegd in de CT-log.
Wat is Ghost Pipe?
Ghost Pipe is het relayprotocol: een versleuteld overdrachtsprotocol op basis van WebSocket; de SDK versleutelt post-quantum (ML-KEM-768). Gegevens staan alleen in RAM en worden vernietigd na de laatste lezing die de link toestaat (één op Community, tot 10 op Firm). De SDK vult elk blok op tot een vaste grootte. Het is ontworpen voor vijandige omgevingen waarin de relay zelf gecompromitteerd kan zijn.
Hoe krijg ik een pgp_-API-sleutel?
paramant.app/signup: maak een gratis account aan en ontvang uw API-sleutel, beveiligd met TOTP. Community-abonnement: 50 overdrachten per maand, 500 MB per bestand verstuurd in blokken van 5 MB, TTL van 1 uur, gewist bij de eerste lezing. Een eenmalige link uit de webapp is één blok, dus die manier stopt bij 5 MB per bestand. Zie /pricing voor de details per abonnement.