What's new in 3.0.0
Version 3.0.0 keeps the wire format, API surface and crypto guarantees of 2.5.x: every 2.5.x client stays compatible: and focuses on the operator and self-host experience. The headline change is structural: the post-quantum primitives now live in their own audited library.
- paramant-core crypto split (M5b). Server-side ML-DSA-65 signing is now provided by paramant-core: a standalone, audit-ready Rust library (BUSL-1.1) the relay consumes through its
@paramant/coreNAPI binding. Client-side ML-KEM-768 still runs in the browser/SDK; the relay never holds a key. See Crypto stack. - Cards-per-product dashboard. The user dashboard is rebuilt around per-product cards, drawing on the existing
/v2/check-key, monitor and audit endpoints: no new trust surface. - Refreshed interactive UI. Dark mode (top-right toggle), real-time status updates and a mobile-friendly layout across the dashboard.
- First-run setup wizard. A web wizard at
/setupreplaces hand-run admin scripts for first-time operators: admin token, TOTP enrolment and the first key from the browser (ADR R005). - Visual admin config.
/admin/settingsexposes relay configuration in the panel, so common changes no longer require editing.envand restarting. - In-browser admin CLI.
/admin/cliis a web terminal for admin debugging: inspect relay state without opening an SSH session. - Crypto-mode negotiation (ADR R006, accepted).
/v2/capabilitiesadvertises a compact core mode (2 algorithms) by default; extended algorithm sets are opt-in. See Crypto stack. - Add-on architecture (ADR R007, draft). A specification for container-isolated integrations: storage mirrors, SIEM exporters, SSO: that work on ciphertext and metadata only, never plaintext or keys.
- Static front-end serving (ADR R011). An opt-in
SERVE_FRONTENDstatic handler lets a single relay serve its own UI for plug-and-play self-hosting. See Self-hosting. - Documented design decisions. Architecture Decision Records R001–R011 capture the choices behind 3.0.0: from the hotfix flow to the crypto split: in the repository under
docs/adrs/.
Quick start
Send your first encrypted file in under 5 minutes. Create a free account here (TOTP protected, key visible in dashboard).
Install the SDK (Python 3.10+ or Node 18+; both ship the same wire-format-v1 encoder)
# Python pip install paramant-sdk # JavaScript / Node npm install paramant-sdk
Send a file: encrypted client-side before it leaves your device
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 only. data, proof = gp.receive(hash_)
Or use the direct HTTP API: no SDK needed:
# 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 (one-time: blob is burned after this) curl https://health.paramant.app/v2/outbound/abc123... \ -H "X-Api-Key: pgp_your_key" -o received.bin
Authentication
Two key types exist: they serve different roles and are never interchangeable.
| Key | Prefix | Who | Purpose |
|---|---|---|---|
| pgp_ | pgp_ | End users | Send/receive files via the API |
| plk_ | plk_ | Relay operators | License key: unlocks >5 users on self-hosted relay |
Pass the pgp_ key in the X-Api-Key header or ?k= query parameter:
X-Api-Key: pgp_your_key_here
# or:
GET /v2/check-key?k=pgp_your_key_here
SDK
Two official SDKs ship the same wire-format-v1 encoder, ML-KEM-768 + ML-DSA-65 by default, and capability negotiation against /v2/capabilities. Pick whichever matches your runtime: they are byte-compatible on the wire.
Install
# Python (3.10+) pip install paramant-sdk # JavaScript (Node 18+) npm install paramant-sdk
Python SDK (class-based)
from paramant_sdk import GhostPipe
gp = GhostPipe(
api_key = "pgp_xxx",
device = "device-001",
sector = "health", # routes to health.paramant.app
)
# Send: returns blob hash
hash_ = gp.send(open("scan.dcm", "rb").read(), ttl=3600)
# Receive: burn-on-read, returns plaintext bytes
data = gp.receive(hash_)
Use cases
PARAMANT has five sector relays: each tuned for its compliance domain. Pick the sector that matches your use case.
healthHealthcare: NEN 7510, DICOM, HL7 FHIR
Send MRI scans, referral letters, and lab results between care providers. health.paramant.app is designed for NEN 7510 compliance: encrypted in transit, RAM-only, EU jurisdiction, per-access CT log proof.
# 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)legalLegal & Notary: eIDAS, KNB
Send signed deeds and court documents that are cryptographically gone after receipt. The CT log provides tamper-evident proof of delivery without storing content.
# 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
iotIndustrial IoT: IEC 62443
PLC and sensor data relay without VPN or direct OT internet exposure. Acts as a post-quantum data diode: OT side pushes outbound only, IT side receives. Fixed 5 MB padding defeats traffic analysis.
# 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 payment file relay with per-transaction Merkle proof for DORA audit trails. finance.paramant.app: EU jurisdiction, no US Cloud Act, no metadata retention.
# 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
All endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /health | Node status, version, uptime, edition | n/a |
| POST | /v2/inbound | Upload encrypted blob from an authenticated client (RAM-only) | Key |
| POST | /v2/anon-inbound | Deprecated. Anonymous upload retained for sdk-js 3.x backward compatibility; removed in a future major release. New integrations must use /v2/inbound. | n/a |
| GET | /v2/outbound/:hash | Download + burn: one retrieval only | Key |
| GET | /v2/status/:hash | Check blob availability without consuming | Key |
| POST | /v2/ack | Confirm delivery, log latency to CT | Key |
| GET | /v2/monitor | Live stats: blobs in flight, ACK rate | Key |
| POST | /v2/ws-ticket | Get 30s one-time WebSocket ticket | Key |
| GET | /v2/stream | WebSocket push: blob_ready events | Ticket / Key |
| POST | /v2/webhook | Register delivery webhook URL | Key |
| POST | /v2/pubkey | Register ML-KEM + ECDH keypair | Key |
| GET | /v2/check-key | Verify key validity and plan | n/a |
| POST | /v2/did/register | Register W3C Decentralized Identity | Key |
| GET | /v2/did/:did | Resolve DID document | n/a |
| GET | /v2/ct/log | Certificate Transparency log (public) | n/a |
| GET | /v2/relays | Registered relay nodes and status | n/a |
| GET | /v2/audit | Merkle audit chain: JSON + CSV export | Key |
| GET | /v2/team/devices | List team devices | Key |
| POST | /v2/team/add-device | Add device to team (Pro+) | Key |
POST /v2/inbound
Upload an encrypted, padded blob. Stored in RAM only: never written to disk. Returns a hash used for retrieval.
Request body (JSON): { "hash": "sha256_of_padded_payload", "payload": "base64_encoded_5mb_blob", "ttl_ms": 3600000 } 200: { "ok": true, "hash": "...", "ttl_ms": 3600000 } 409: hash already exists 503: relay at capacity (memory limit)
TTL is clamped to plan maximum. The hash field is the SHA-256 of the padded payload (not the original file). The SDK computes this automatically.
GET /v2/outbound/:hash
One retrieval only. After this call the blob buffer is overwritten with random bytes and removed from RAM. There is no recovery.
GET /v2/outbound/abc123...
X-Api-Key: pgp_xxx
200: binary blob (always 5MB padded)
404: burned, expired, or never existed
403: API key mismatch
GET /v2/stream
WebSocket endpoint for real-time blob_ready push notifications. Use a one-time ticket to avoid exposing the key in the URL.
// 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
{ "hash": "blob_hash", "callback_url": "https://you.com/hook" }
Relay POSTs to your callback on delivery:
{ "event": "blob_retrieved", "hash": "...", "ts": "2026-04-14T..." }
Webhooks are signed with X-Paramant-Sig (HMAC-SHA256 of the body using your API key as the secret). Reject unsigned callbacks.
GET /health
{
"ok": true,
"version": "3.0.0",
"sector": "health",
"edition": "licensed",
"uptime_s": 3600,
"license_expires": "2027-01-01"
}
Self-hosting: Docker Compose
Community Edition is free forever for up to 5 users. One docker compose up starts 6 containers: five sector relays and an admin panel.
Requirements: Ubuntu 22.04+ / Debian 12+ · Docker 24+ · 1 GB RAM min · swap disabled · a domain with wildcard DNS
# 1. Clone git clone https://github.com/Apolloccrypt/paramant-relay cd paramant-relay # 2. Configure cp .env.example .env echo "ADMIN_TOKEN=$(openssl rand -hex 32)" >> .env nano .env # set TOTP_SECRET and RESEND_API_KEY if desired # 3. Launch docker compose up -d # 4. Verify curl http://localhost:3000/health # → {"ok":true,"version":"3.0.0","sector":"relay"}
Container port map
| Container | Host port | Sector | Recommended domain |
|---|---|---|---|
| relay-main | 127.0.0.1:3000 | relay | relay.your-domain.com |
| relay-health | 127.0.0.1:3001 | health | health.your-domain.com |
| relay-finance | 127.0.0.1:3002 | finance | finance.your-domain.com |
| relay-legal | 127.0.0.1:3003 | legal | legal.your-domain.com |
| relay-iot | 127.0.0.1:3004 | iot | iot.your-domain.com |
| admin | 127.0.0.1:4200 | n/a | your-domain.com/admin/ |
Key .env variables
| Variable | Required | Description |
|---|---|---|
| ADMIN_TOKEN | Yes | Admin panel password (min 32 chars, hex) |
| TOTP_SECRET | Recommended | Base32 TOTP secret: enables MFA on admin login |
| PLK_KEY | No | License key: unlocks >5 users (Community = free, max 5) |
| RESEND_API_KEY | No | Email provider key (for key delivery notifications) |
| LOG_LEVEL | No | info (default) · debug · warn · error |
| RAM_LIMIT_MB | No | Blob storage memory cap per container (default 1024) |
nginx & TLS
Use system nginx as a TLS terminator in front of the Docker containers. Certbot handles certificate renewal automatically.
# 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
Add an nginx server block for each subdomain that proxies to the matching container port:
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)
First user
# 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 enforces a hard cap of 5 users: the 6th add returns HTTP 402. Add a PLK_KEY to .env to unlock unlimited users.
Guided setup: a first-run web wizard at /setup replaces hand-run admin scripts for first-time operators: generating the admin token, enrolling TOTP and minting the first key from the browser. The wizard is gated to first boot (no keys yet) or an explicit SETUP_MODE flag, and runs deeper /v2/health/deep and DNS checks before declaring all-systems-go (design: ADR R005). Once running, /admin/settings exposes relay configuration visually: no more editing .env by hand: and /admin/cli gives admins a browser terminal for debugging without SSH.
Plug-and-play serving: set SERVE_FRONTEND=1 to have a single relay serve its own UI from a read-only static handler, so a fresh self-host needs no separate web server (opt-in, default off; ADR R011). Set CRYPTO_MODE to choose the algorithm set the relay advertises on /v2/capabilities: core (the default: ML-KEM-768 + ML-DSA-65) or extended for the full opt-in set (ADR R006).
Upgrade
cd /opt/paramant-relay
git pull --ff-only
docker compose up -d --build
# Named volumes (users.json, CT log, relay identity) are preserved
Note: User data and the CT log persist in Docker named volumes across upgrades. The relay identity keypair (ML-DSA-65) is generated once on first boot and stored at /data/relay-identity.json: do not delete this volume or the relay will lose its registered identity. Since v3.0.0 the ML-DSA-65 signing that backs this identity comes from paramant-core, the relay's audit-ready Rust crypto dependency (consumed via the @paramant/core NAPI binding): it ships inside the Docker image, so no extra install step is needed.
Admin scripts
Admin scripts ship with the relay repository. After cloning paramant-relay, the server-side admin tool lives under deploy/: run python3 deploy/paramant-admin.py [command] directly. Operator helpers (key add/revoke, first-boot setup) live under scripts/. No system-wide install package is currently provided: the SDK packages on PyPI (paramant-sdk) and npm (paramant-sdk) are the canonical client integrations.
Add-ons: an add-on architecture is specified for integrations that live just beyond the relay core: storage mirrors, notification bridges, SIEM exporters, OIDC/SAML SSO. Container-isolated, manifest-declared capabilities, HomeAssistant-style, working on ciphertext and metadata only (never plaintext or keys). Specification: ADR R007, draft.
CLI reference
| Command | Description |
|---|---|
| paramant-send | Encrypt and upload a file or stdin |
| paramant-receive | Download and decrypt by hash |
| paramant-watch | Watch a directory and auto-send new files |
| paramant-stream | Listen for incoming blobs via WebSocket |
| paramant-admin | Manage users, keys, and relay config |
| paramant-scan | Discover relay nodes on the network |
# Example: send a file and wait for ACK paramant-send --key pgp_xxx --sector health --ack scan.dcm # Example: stream mode (process persists, receives all blobs) paramant-stream --key pgp_xxx --sector health --forward https://pacs.hospital/api
ParamantOS: 39 operator tools
ParamantOS is a hardened Linux distribution (NixOS + Mint editions) for relay operators. All 39 tools are pre-installed.
| Category | Command | What it does |
|---|---|---|
| Setup & diagnostics | paramant-setup | First-boot wizard: password, relay URL, API key |
| paramant-help | Full command reference | |
| paramant-info | System overview: OS, relay version, uptime | |
| paramant-doctor | Automated health check for relay + security | |
| paramant-install | Interactive disk installer | |
| Relay control | paramant-status | Relay health, version, edition, uptime |
| paramant-relay-setup | Clone relay repo + configure .env + start Docker | |
| paramant-relay-ctl | Privileged relay service control (start/stop/reload) | |
| paramant-restart | Restart the relay | |
| paramant-dashboard | Live TUI dashboard: stats, connections, throughput | |
| paramant-logs | Live relay log stream | |
| paramant-update | Check for relay updates and show upgrade path | |
| Sector tools | paramant-referral | Healthcare referral: NEN 7510, HL7 FHIR, DICOM |
| paramant-notary | Legal document transport: eIDAS, KNB notary | |
| paramant-legal | Court document relay (replaces Zivver/e-Court) | |
| paramant-payslip | HR payslip distribution: GDPR compliant bulk send | |
| paramant-firmware | IoT/body cam firmware updates: IEC 62443 | |
| paramant-cra | Software supply chain relay: EU CRA 2027, SBOM | |
| paramant-ticket | One-time transit ticket issuer and verifier | |
| CT log & verification | paramant-verify-sth | Verify ML-DSA-65 signed tree head against relay |
| paramant-receipt | View or verify a delivery receipt | |
| paramant-verify-peers | Cross-check STH consistency across all peer relays | |
| Key management | paramant-keys | List all API keys |
| paramant-key-add | Add a new API key | |
| paramant-key-revoke | Revoke an API key | |
| paramant-license | Show license status and upgrade path | |
| Network | paramant-ip | IP addresses, interfaces, relay accessibility |
| paramant-ports | Firewall rules and listening ports | |
| paramant-wifi | Interactive WiFi manager | |
| paramant-scan | Discover paramant relay nodes via registry | |
| Security | security-status | All security layers at a glance |
| paramant-security | Firewall, SSH, kernel hardening status | |
| paramant-verify | Out-of-band TOFU fingerprint verification | |
| paramant-hybrid-check | Verify relay hybrid post-quantum mode support | |
| Backup & data | paramant-backup | Backup relay keys and CT log |
| paramant-restore | Restore from backup | |
| paramant-export | Export audit log to USB drive | |
| paramant-data-ctl | Privileged relay data-dir management | |
| paramant-cron | Manage systemd timers for relay maintenance |
Download ParamantOS: NixOS edition (v2.4.5) · Mint edition (v1.0-β)
Crypto stack
All encryption happens client-side. The relay never sees plaintext and never holds a private key.
| Algorithm | Role | Standard |
|---|---|---|
| ML-KEM-768 | Post-quantum key encapsulation | NIST FIPS 203 |
| ECDH P-256 | Classical key exchange (hybrid, defence-in-depth) | NIST SP 800-56A |
| AES-256-GCM | Symmetric authenticated encryption | NIST FIPS 197 |
| HKDF-SHA256 | Key derivation from hybrid KEM output | RFC 5869 |
| SHA3-256 | CT log Merkle hashing · DID hashes | NIST FIPS 202 |
| ML-DSA-65 | Relay identity signatures (relay-to-relay auth) | NIST FIPS 204 |
| Argon2id | Password-protected blob derive | RFC 9106 |
Browser-side encryption runs as Rust/WASM compiled from crypto-wasm/. The WASM binary is SHA-256 verified at runtime before first use. The relay JS code has no access to decryption keys at any point.
The two halves of the stack run different builds of the same Rust crypto. ML-KEM-768 key encapsulation stays client-side (browser WASM / SDK): the relay never performs a key exchange and never holds a private key. ML-DSA-65 runs server-side for the relay identity keypair, signed delivery receipts and signed tree heads (STH), and for verifying device and receipt signatures. Since the M5b migration (2026-05-27) that server-side ML-DSA-65 is provided by paramant-core: a standalone, audit-ready Rust crypto library (BUSL-1.1) consumed by the relay through its @paramant/core NAPI binding.
Burn-on-read
After GET /v2/outbound/:hash the relay immediately overwrites the blob buffer with cryptographically random bytes and removes the entry from memory. Encrypted payload data is never written to disk at any point during the transfer.
The CT log persists cryptographic hashes (Merkle leaf hashes, tree hashes, device hashes) to disk: never payload content. Even a complete disk image of the relay server reveals no file contents.
Two-step burn: first the blob is zeroed in-place, then the Map entry is deleted. This prevents a brief window where a partial read could occur.
5 MB fixed padding (ParaShare)
ParaShare authenticated blobs are padded to exactly 5,242,880 bytes (5 MiB) with cryptographically random bytes before upload. An observer on the network cannot determine the size, type, or content of the transferred file. This is effective DPI masking for classified data flows.
Certificate Transparency log
Every transfer hash and device registration is appended to a public Merkle tree. The root hash changes with every write and is publicly verifiable. Anyone can prove a transfer occurred without learning what was transferred.
GET https://health.paramant.app/v2/ct/log?limit=100&offset=0
{
"root": "cf7436a9efa4ee9a...",
"entries": [
{ "index": 56, "leaf_hash": "ecb779...", "tree_hash": "cf7436...",
"device_hash": "ce56ff...", "ts": "2026-04-14T19:07:57Z" }
]
}
Live CT log: paramant.app/ct-log →
W3C Decentralized Identity
Devices can register a W3C DID backed by ML-DSA-65 and ECDH keys. Once registered, subsequent requests are signed: no API key in the 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
Team management
# Create an API key (admin script) python3 scripts/paramant-admin.py add \ --label "scanner-room-3" --plan pro --email admin@hospital.nl # Sync to all relay containers python3 scripts/paramant-admin.py sync # Revoke a key immediately python3 scripts/paramant-admin.py revoke --label "scanner-room-3" python3 scripts/paramant-admin.py sync
Key changes are zero-downtime: the relay hot-reloads users.json on sync without restart. Revoked keys receive WebSocket close code 4401 within seconds.
Retention policy
| Plan | Default TTL | Maximum TTL | Max file size |
|---|---|---|---|
| Free (pgp_) | 1 hour | 1 hour | 5 MB (padded) |
| Pro | 1 hour | 24 hours | 5 MB |
| Enterprise | 1 hour | 7 days | 5 MB |
Relay sectors
All sector nodes run identical Ghost Pipe relay software. Sectors are routing domains: they provide traffic isolation and sector-specific compliance documentation. The crypto stack and API surface are identical.
| Sector | URL | Primary use | Compliance |
|---|---|---|---|
| health | health.paramant.app | DICOM, patient records, vitals | NEN 7510, GDPR |
| legal | legal.paramant.app | Contracts, notary, evidence | AVG, eIDAS |
| finance | finance.paramant.app | ISO 20022, compliance data | NIS2, DORA |
| iot | iot.paramant.app | SCADA, PLCs, sensor streams | IEC 62443 |
Security: threat model
The relay is untrusted by design. Security does not depend on trusting the operator.
| What a compromised relay can do | What it cannot do |
|---|---|
| Deny or delay service | Read file contents (no decryption key) |
| Learn transfer timing and frequency | Substitute a registered ML-KEM public key (CT log prevents rollback) |
| Observe that a ParaShare blob arrived (all fixed 5 MB, content opaque) | Forge relay signatures (ML-DSA-65 key is per-relay, signed) |
| Block a specific device hash | Decrypt any stored ciphertext |
The threat model assumes network-level attackers, compromised relay operators, and post-quantum adversaries. Encryption uses a hybrid scheme (ML-KEM-768 + ECDH P-256) so that breaking either primitive is not sufficient: both must be broken simultaneously.
Key insight: The relay stores encrypted blobs it cannot decrypt and Merkle hashes it cannot forge. A warrant served on the relay operator yields ciphertext and metadata: not plaintext.
Jurisdiction
Managed relay infrastructure runs exclusively on Hetzner Nuremberg, Germany. EU/GDPR jurisdiction applies. No US Cloud Act jurisdiction: Hetzner is a German company with no US parent. No metadata leaves the EU.
Security audits
| Date | Auditor | Findings | Status |
|---|---|---|---|
| Apr 2026 | R. Zwarts (verification) | 14 total (1H · 8M · 5L) | All resolved: e6f216d |
| Apr 2026 | R. Zwarts (independent) | 6 total (3H · 3M) | All resolved: 0db3ef0 |
| Apr 2026 | Ryan Williams · Smart Cyber Solutions | 4C · 5H · 6M · 5L | All resolved: 0db3ef0 |
All findings are publicly documented in SECURITY.md. To report a vulnerability: privacy@paramant.app.
Compliance: NIS2 / DORA
NIS2 (EU 2022/2555) applies to operators of essential services in the EU. PARAMANT supports NIS2 compliance through:
- Post-quantum encryption (future-proof cryptographic resilience)
- Merkle CT log: tamper-evident audit trail for every transfer
- EU-only infrastructure (Hetzner DE): no data leaves EU jurisdiction
- Burn-on-read: minimises data retention exposure
- Zero-downtime key revocation: immediate incident response capability
DORA (EU 2022/2554) applies to financial entities. The finance.paramant.app sector is designed with DORA in mind: per-transaction Merkle proof, no US Cloud Act jurisdiction, ISO 20022 compatible.
View full NIS2 compliance documentation →
NEN 7510 (Healthcare)
NEN 7510 is the Dutch standard for information security in healthcare. The health.paramant.app sector is designed for NEN 7510 compliance:
- Encrypted in transit and at rest (technically: RAM only, never disk)
- Access logging via CT log: cryptographic proof of every access event
- Device identity registration (DID): traceable per-device access
- Key-based access control: per-user API keys with revocation
- Hetzner DE: EU GDPR jurisdiction, suitable for patient data routing
View full NEN 7510 compliance documentation →
IEC 62443 (Industrial / OT)
IEC 62443 is the international standard for operational technology security. The iot.paramant.app sector supports IEC 62443 deployments:
- Acts as a quantum-safe data diode: PLCs/sensors push data without opening inbound ports
- No VPN, no certificates on OT side: works with Raspberry Pi and any Linux device
- Fixed 5MB padding: traffic analysis cannot distinguish heartbeats from payloads
- Network segmentation: OT side connects outbound only; IT side receives inbound only
- ML-DSA-65 device signatures: cryptographic device authentication
View full IEC 62443 compliance documentation →
Operator scripts
Self-host operators get a set of command-line helpers under scripts/ in the paramant-relay repo. SSH into your relay host and run them from the repo root. For most of these there is an in-browser equivalent at /admin/cli (admin login required).
- Keys.
scripts/paramant-key-add.sh: provision an API key. - Signing.
scripts/paramant-sign: ParaSign CLI: sign a document (ML-DSA-65, client-side) and notarise it into a.psignenvelope, or verify one. See ADR R017. - Deploy safety.
scripts/post-deploy-verify.sh: post-deploy smoke test (exit 2 on critical failure);scripts/rollback-3.0.0.sh: roll back to the previously-built relay image. - Backups.
scripts/backup-users-json.sh: age-encrypted backup of relay key metadata (already wired into cron, 03:15 daily; decrypt only in tmpfs).
# 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
Frequently asked questions
PLK_KEY to .env to unlock unlimited users. Source is available under BUSL-1.1.pgp_ keys are for end users: they authenticate API calls to send and receive files. plk_ keys are relay license keys for operators: they go in .env, not in API calls, and unlock unlimited user capacity. They are never the same key and cannot be substituted for each other.curl -fsSL https://paramant.app/install-pi.sh | bash script detects your Pi model, installs Docker, disables swap, and starts the relay. Minimum 512 MB RAM for a single-sector relay.