X25519MLKEM768 · TLS 1.3 · Post-Quantum · Cloud SaaS

PQ-Proxy Cloud — Setup Guide & API Reference

Point your domain at PQ-Proxy. Every connection is automatically protected with X25519MLKEM768.
Dashboard: proxy.fipsign.dev  ·  API base: https://proxy-api.fipsign.dev/api/v1

00 Prerequisites
What you need before starting.
Requirements
RequirementDetails
AccountRegister at proxy.fipsign.dev — no credit card required for the 7-day trial
A domain you controlYou need to be able to update the DNS A record for the domain you want to protect
A backendAny HTTP or HTTPS server — cloud, on-premise, or serverless. Must be reachable from the internet.
API keyAvailable in the dashboard under Settings. Format: pqp_live_...
Note: The trial includes 1 domain. Add wallet credit to protect additional domains at $0.04/hr each.
01 Add your domain
Configure your domain and backend in the PQ-Proxy dashboard.
Dashboard → Domains → + Add Domain
FieldValueNotes
Public domainapi.yourcompany.comThe domain your clients connect to. Must have an A record pointing to PQ-Proxy.
Backend hostbackend.yourcompany.comWhere PQ-Proxy forwards traffic after terminating TLS. Must be a public hostname or IP — private ranges (10.x, 192.168.x, 172.16–31.x), loopback (localhost, 127.x), and cloud metadata addresses (169.254.x) are not allowed.
Backend port443Use 443 for HTTPS backends, 80 for HTTP. Default: 443.
Backend TLS✓ enabledEnable if your backend serves HTTPS. Disable for plain HTTP backends. Required for HTTP/2 and gRPC backends — when enabled, PQ-Proxy negotiates HTTP/2 via ALPN automatically.
Note: PQ-Proxy automatically requests a Let's Encrypt certificate for your domain after you add it. The certificate is provisioned within seconds on the first connection — you may see a slightly longer TLS handshake on the first request while the certificate is being obtained.
02 DNS setup
Point your domain's A record to PQ-Proxy's IP address.
Add or update the A record
# In your DNS provider (Cloudflare, Route53, etc.)
Type:  A
Name:  api        # or @ for the root domain
Value: 137.66.56.190
TTL:   3600
Important: If your domain is on Cloudflare, set the proxy status to DNS only (gray cloud). The orange proxy would terminate TLS at Cloudflare before it reaches PQ-Proxy — disabling the post-quantum handshake.
Verify DNS propagation
# Check that the A record resolves correctly
dig api.yourcompany.com A +short
# Should return: 137.66.56.190
03 Verify the connection
Confirm that post-quantum TLS is active on your domain.
Test with curl
curl -sv https://api.yourcompany.com 2>&1 | grep -i "ssl\|tls\|handshake\|x25519"
Expected output
* SSL connection using TLSv1.3 / X25519MLKEM768 * Server certificate: * subject: CN=api.yourcompany.com * issuer: C=US, O=Let's Encrypt, CN=R11
X25519MLKEM768 in the SSL connection line confirms that post-quantum key exchange is active. Your backend is now protected.
Check the dashboard

In the dashboard, go to Health — your domain should show ● healthy once the first connection is made.

04 API authentication
All API endpoints require an API key passed in the X-Api-Key header.
Get your API key

Go to Dashboard → Settings. Your API key starts with pqp_live_.

Usage
# Pass the API key in every request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant \
  -H "X-Api-Key: pqp_live_YOUR_KEY_HERE"
Note: API key endpoints are read-only — they allow monitoring and data export but not domain management. To create, edit, or delete domains, use the dashboard. To rotate your API key, go to Dashboard → Settings → Rotate API key. The old key is immediately invalidated.
05 GET /tenant
Returns account information for the authenticated tenant.
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": { "email": "[email protected]", "company_name": "Acme Corp", // null if not set "plan": "trial", // "trial" | "active" "subscription_status": "active", // "trial" | "active" | "grace" | "suspended" "trial_ends_at": "2026-06-30T13:43:18Z", // null if not on trial "created_at": "2026-06-23T13:43:18Z" }, "error": null }
06 GET /tenant/domains
Returns all domains configured for the authenticated tenant.
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant/domains \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": [ { "id": "5206aaec-1368-495e-9fe2-c6b7acf5cd11", "tenant_id": "16560e46-44bb-4388-afd0-e38e48b79ffc", "domain": "api.yourcompany.com", "backend_host": "backend.yourcompany.com", "backend_port": 443, "backend_tls": true, "active": true, "created_at": "2026-06-23T17:28:58Z" } ], "error": null }
07 GET /tenant/domains/:domain_id/certificate
Returns certificate information for a specific domain. Use the domain id from /tenant/domains.
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant/domains/DOMAIN_ID/certificate \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": { "domain": "api.yourcompany.com", "cert_source": "acme", // "acme" | "byoc" "expires_at": "2026-09-21T19:48:19Z" }, "error": null }
cert_source: acme means the certificate was issued automatically by Let's Encrypt. byoc means you uploaded your own certificate.
08 GET /tenant/billing
Returns wallet balance, daily cost, and subscription status.
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant/billing \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": { "subscription_status": "active", // "trial" | "active" | "grace" | "suspended" "balance_cents": 5000, // wallet balance in USD cents "balance_usd": 50.0, // wallet balance in USD "domains_active": 2, // number of active domains "daily_cost_cents": 192, // $1.92/day for 2 domains "daily_cost_usd": 1.92, "total_consumed_cents": 28, // total spent since account creation "total_consumed_usd": 0.28, "trial_ends_at": "2026-06-30T13:43:18Z", // null if not on trial "low_balance_warning": false // true if balance < 7 days of cost }, "error": null }
Tip: Poll low_balance_warning to trigger wallet top-ups before service is interrupted. The wallet is charged $0.04/hour per active domain.
09 GET /tenant/billing/transactions
Returns the transaction history for the wallet — top-ups and hourly consumption entries. Supports pagination with ?page=1&per_page=50 (default: 50, max: 200).
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant/billing/transactions \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": [ { "id": "44f6fe83-5974-4631-9266-153341d8b9d2", "type": "topup", // "topup" | "consumption" "amount_cents": 5000, // positive = credit, negative = debit "description": "Top-up: $50.00 USD", "created_at": "2026-06-24T17:05:43Z" }, { "id": "a1b2c3d4-...", "type": "consumption", "amount_cents": -8, // -$0.08 for 2 domains × $0.04/hr "description": "Hourly consumption: 2 domain(s)", "created_at": "2026-06-27T12:00:00Z" } ], "error": null }
10 GET /tenant/metrics
Returns connection metrics aggregated across all domains.
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant/metrics \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": { "total_connections": 239, "total_bytes_sent": 900455, "total_bytes_received": 75098, "connections_last_24h": 25, "connections_last_7d": 239, "avg_tls_handshake_ms": 494.3, "avg_backend_connect_ms": 12.7, "algorithm": "X25519MLKEM768", // dominant algorithm across connections "by_domain": [ /* same fields per domain */ ] }, "error": null }
11 GET /tenant/connections
Returns paginated connection logs. Each entry represents one TLS connection to a protected domain.
Request
curl -s "https://proxy-api.fipsign.dev/api/v1/tenant/connections?page=1&per_page=20" \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": [ { "id": "e1d4f76b-efd6-4ad8-996d-9fa30e514ade", "domain": "api.yourcompany.com", "peer_addr": "181.47.8.234", "bytes_sent": 4096, "bytes_received": 645, "connected_at": "2026-06-27T09:58:49Z", "duration_ms": 59, "tls_handshake_ms": 373, "backend_connect_ms": 3, "algorithm": "X25519MLKEM768" // or "X25519" for classical TLS 1.3 fallback } ], "error": null }
12 GET /tenant/connections/export
Exports all connection logs as a CSV file.
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant/connections/export \
  -H "X-Api-Key: pqp_live_..." \
  -o connections.csv
CSV format
id,domain,peer_addr,bytes_sent,bytes_received,algorithm,connected_at,duration_ms,tls_handshake_ms,backend_connect_ms
13 GET /tenant/health
Returns the current health status of all domains.
Request
curl -s https://proxy-api.fipsign.dev/api/v1/tenant/health \
  -H "X-Api-Key: pqp_live_..."
Response
{ "success": true, "data": [ { "domain": "api.yourcompany.com", "status": "healthy", // "healthy" | "unhealthy" | "pending" "latency_ms": 2, "last_checked_at": "2026-06-27T11:57:40Z", "last_error": null, "consecutive_failures": 0 } ], "error": null }
Health checks run every 60 seconds. If your backend goes down, PQ-Proxy sends an alert email and continues retrying.
14 BYOC — bring your own certificate
Upload your own TLS certificate instead of using the automatic Let's Encrypt certificate.
Upload via dashboard

Go to Dashboard → Domains, click Upload custom cert on your domain, and paste your PEM-encoded certificate and private key.

Requirements
FieldFormatNotes
CertificatePEMFull chain preferred — include intermediate certificates
Private keyPEMRSA or ECDSA. The key must match the certificate.
Important: The private key is stored encrypted at rest and never exposed via any API endpoint.
15 FAQ
Common questions about PQ-Proxy Cloud setup and operation.
Does PQ-Proxy support HTTP/2?

Yes. Both directions support HTTP/2. The client-to-proxy connection supports HTTP/1.1 and HTTP/2 over TLS 1.3 with X25519MLKEM768. gRPC backends are supported when Backend TLS is enabled.

What if the client doesn't support X25519MLKEM768?

PQ-Proxy falls back to X25519 (classical Diffie-Hellman) for clients that don't support post-quantum key exchange. The connection is still TLS 1.3. Chrome, Firefox, and curl support X25519MLKEM768 by default.

What is the proxy IP for DNS setup?
137.66.56.190
Can I use PQ-Proxy with Cloudflare?

Yes, but set the Cloudflare proxy status to DNS only (gray cloud). If the orange cloud proxy is active, Cloudflare terminates TLS before reaching PQ-Proxy — disabling the post-quantum handshake.

Does PQ-Proxy support HTTP/3?

No. PQ-Proxy uses TLS 1.3 over TCP. HTTP/3 (QUIC) is not supported — a deliberate choice, as UDP is frequently blocked in enterprise networks.

X25519MLKEM768 · NIST FIPS 203 · ML-DSA-65 · NIST FIPS 204 · On-Premise

PQ-Proxy On-Premise — Setup Guide & API Reference

Install PQ-Proxy on your own server. Offline license verification with ML-DSA-65.
Self-service: onprem.fipsign.dev  ·  API base: http://YOUR_SERVER_IP:9090/api/v1

00 Prerequisites
What you need before installing PQ-Proxy On-Premise.
RequirementDetails
ServerLinux server with a public IP address. The installer runs as root in /root.
Docker EngineVersion ≥ 23 with Docker Compose v2. If not installed, the installer will install it automatically via get.docker.com.
Port 443Open inbound — TLS connections from your clients arrive here.
Port 80Open inbound — required only if you use ACME / Let's Encrypt for automatic certificate provisioning.
Port 9090Open inbound from your own IP — the management dashboard. Not exposed to the internet by default.
Outbound HTTPSThe proxy sends a heartbeat to proxyonprem.fipsign.dev every 24 hours. This must not be firewalled.
license.pqpYour license file. Obtain a trial or purchase a license at onprem.fipsign.dev.
01 Get a license
Obtain a trial or standard license before installation.
Trial — 14 days, 1 domain, all features

Go to onprem.fipsign.dev/trial, enter your corporate email, and click Request trial. A verification link will be sent to your email. After clicking it, you will receive the license.pqp file at the same address. The download link is single-use and expires in 24 hours.

Standard — $499/year, unlimited domains

Go to onprem.fipsign.dev/purchase and complete the payment via OxaPay (crypto). The license.pqp file will be sent to your email after payment is confirmed. The download link is single-use and expires in 48 hours.

Note: The license.pqp file is a JSON document signed with ML-DSA-65 (NIST FIPS 204). Verification is 100% offline — the proxy binary has the public key embedded at compile time. The file cannot be forged or modified without invalidating the signature.
Copy the license to your server
# From your local machine
scp license.pqp root@YOUR_SERVER_IP:/root/
02 Installation
The installer downloads the stack, asks configuration questions, and starts the containers.
Download and run the installer
# On your server, as root
curl -fsSL https://proxyonprem.fipsign.dev/install -o install.sh && bash install.sh
Important: Do not pipe directly to bash (curl ... | bash). Download first so you can inspect the script before running it.
Installer wizard — questions and defaults
QuestionDefaultNotes
Organization nameShown in the dashboard sidebar and alert emails.
Operator emailUsed for SMTP alerts if configured.
Public URL(blank)Optional. If set (e.g. https://proxy.mycompany.com), alert emails include a direct link to the dashboard.
Database1 — Bundled PostgreSQLBundled is recommended. Option 2 allows an external PostgreSQL URL (AWS RDS, Supabase, Neon, etc.).
TLS port443Port where the post-quantum proxy listens for client connections.
HTTP port80Used for ACME HTTP-01 challenges and HTTP→HTTPS redirects.
Server exposure1 — Direct1 = direct to internet (socket IP). 2 = behind nginx/HAProxy/NLB (Proxy Protocol). 3 = behind Cloudflare/ALB/CDN (X-Forwarded-For). Affects how the real client IP is determined.
Allow private backendsYY = backends on private LAN IPs (10.x, 192.168.x, 172.16–31.x) are accepted. N = SSRF protection is active — only public IPs and hostnames allowed.
Enable ACMENY = Let's Encrypt certificates are provisioned automatically. Requires port 80 to be publicly reachable. N = upload certificates manually (BYOC) from the dashboard.
Session duration24 hoursHow long a dashboard login session lasts before requiring re-authentication.
Configure SMTPNY = enables OTP email login and email alerts. Requires host, port, username, password, from address, and TLS mode (starttls | tls | none).
Enable PrometheusNY = exposes a Prometheus metrics endpoint.
Connection log retention30 daysConnection logs older than this are automatically purged.
Enable email alertsNRequires SMTP to be configured. Sends alerts on certificate expiry and license events.
Webhook URL(blank)Optional. Slack, PagerDuty, or any HTTP endpoint. Signed with HMAC-SHA256.
Cert expiry warning30 daysAlert is sent when a certificate expires in fewer than this many days.
Cert expiry critical7 daysCritical alert threshold.
Check for updatesYY = the management API checks for new versions every 24 hours (configurable). Does not auto-update.
Update check interval24 hoursHow often to poll for new versions.
What the installer creates
FileDescription
/root/compose.ymlDocker Compose stack definition — three services: db (PostgreSQL), api (management-api + dashboard), proxy (proxy-core).
/root/config.tomlFull configuration file. To change any setting, edit this file and run bash install.sh --reconfigure.
/root/.envEnvironment variables: INTERNAL_SERVICE_TOKEN, DB_PASSWORD, LICENSE_FILE, API_PORT.
Admin token — shown only once

At the end of the installation, the terminal prints your admin token:

pqp_onprem_a1dedb3d9c40689d4433fb4af585df690dd06565b5af7095d45b9c5f76761d88
Copy this token immediately. It is shown only once. If you lose it and have an active dashboard session, rotate it under Settings → Rotate admin token. If you have no active session and no SMTP configured, run this command on your server to generate a new token:

docker compose exec api pq-proxy-api reset-token
Access the dashboard
http://YOUR_SERVER_IP:9090/dashboard

Log in by pasting the admin token. If SMTP is configured, you can also log in with an OTP sent to the operator email.

03 Admin token & dashboard login
The admin token is the primary authentication method. SMTP-based OTP is available when SMTP is configured.
Login methods
MethodRequiresDescription
Static tokenAlways availablePaste the admin token (pqp_onprem_...) in the login screen. Works without SMTP.
OTP emailSMTP configuredEnter the operator email — a 6-digit OTP valid for 10 minutes is sent. Rate limited to 5 attempts per 15 minutes per IP.
Rotate the admin token (requires active session)
# Requires an active dashboard session
POST /api/v1/admin/rotate-token

The old token is immediately invalidated. The new token is returned in the response — copy it before closing the session.

Reset the admin token (no session required)

If you have lost the token and have no active session, run this command on your server via SSH. It requires no dashboard access, no SMTP, and no DB knowledge — just SSH to your server as root:

# On your server, as root
docker compose exec api pq-proxy-api reset-token

A new token is generated, saved to the database, and printed to the terminal. The old token is immediately invalidated.

Invalidate all sessions
POST /api/v1/admin/invalidate-sessions

Forces all active sessions to log out. Useful if you suspect a session was compromised.

Session duration is configured at install time (default 24 hours). Change it via reconfiguration or by editing [auth] session_duration_hours in config.toml and restarting the API container.
04 Add a domain
Configure the first domain to proxy post-quantum TLS to your backend.
Dashboard → Domains → Add Domain
FieldValueNotes
Public domainapi.yourcompany.comMust be a valid FQDN (contains at least one dot). Labels must be ASCII alphanumeric or hyphens, max 63 chars each, max 253 total.
Backend host10.0.0.5 or backend.internalWhere the proxy forwards decrypted traffic. Private IPs (10.x, 192.168.x, 172.16–31.x) are allowed when allow_private_backends = true (the default). Set to false for SSRF protection.
Backend port443Default is 443. Use 80 or any other port for plain HTTP backends.
Backend speaks TLS☐ or ✓Enable if your backend serves HTTPS. The proxy will establish a TLS connection to the backend. Disable for plain HTTP backends.
DNS setup

Point the domain's A record to your server's public IP:

Type:  A
Name:  api
Value: YOUR_SERVER_IP
TTL:   3600
Domain limit: The trial license allows 1 domain. The standard license allows unlimited domains. Adding a domain when the limit is reached returns an error — upgrade your license at onprem.fipsign.dev/purchase.
Domain reload: The proxy refreshes its domain list from the management API every 30 seconds. New domains are active within 30 seconds of being added — no restart required.
05 TLS certificates
Two options: automatic via ACME / Let's Encrypt, or manual upload (BYOC).
Option 1 — ACME / Let's Encrypt (automatic)

Enable ACME during installation or via reconfiguration. The proxy issues HTTP-01 challenges on port 80. Once a domain is added, the certificate is provisioned on the first connection. Renewal is checked every 12 hours — certificates are automatically renewed when they have fewer than 10 days remaining. The expiry date is read directly from the certificate X.509 field, so any certificate duration is supported.

Alert vs renewal: The certificate expiry alert fires at 30 days remaining (configurable at install time) — this warns you that renewal will happen soon. The actual renewal fires at 10 days remaining. If the renewal fails (e.g. port 80 blocked), the critical alert fires at 7 days remaining, giving you time to intervene before the certificate expires.
Requirement: Port 80 must be publicly reachable from the internet. Let's Encrypt must be able to reach your server at http://api.yourcompany.com/.well-known/acme-challenge/....
Option 2 — BYOC (bring your own certificate)

Go to Dashboard → Domains, click Upload BYOC on your domain, and paste your PEM-encoded certificate and private key.

FieldFormatNotes
cert_pemPEMFull chain preferred — include intermediate certificates. The proxy parses the expiry date automatically.
key_pemPEMRSA or ECDSA. Must match the certificate. Stored encrypted at rest.
Check certificate status
# List all certificates with expiry dates
GET /api/v1/certificates
Response
{ "success": true, "data": [ { "domain": "api.yourcompany.com", "cert_source": "acme", // "acme" | "byoc" "expires_at": "2026-09-21T19:48:19Z", "obtained_at": "2026-06-23T10:00:00Z" } ] }
06 Verify post-quantum TLS
Confirm that X25519MLKEM768 is active on your domain.
Test with curl
curl -sv https://api.yourcompany.com 2>&1 | grep -i "ssl\|tls\|x25519"
Expected output
* SSL connection using TLSv1.3 / X25519MLKEM768 * Server certificate: * subject: CN=api.yourcompany.com
X25519MLKEM768 confirms that post-quantum key exchange is active. Your backend is now protected by NIST FIPS 203.
Verify in the browser

Open your domain in Chrome or Brave, press F12, go to Security tab. The connection line should show TLS 1.3, X25519MLKEM768, and AES_128_GCM.

What happens for clients that don't support X25519MLKEM768?

The proxy automatically falls back to X25519 (classical Diffie-Hellman). The connection is still TLS 1.3 — just without the post-quantum key exchange. No configuration needed.

07 License states & heartbeat
How the license is verified, what states it can be in, and how the heartbeat works.
Offline verification at startup

Every time the proxy-core container starts, it reads license.pqp from disk and verifies the ML-DSA-65 signature against the public key embedded in the binary. If the signature is invalid or the file is missing, the proxy does not start.

License states
StateConditionBehavior
validMore than 30 days until expiryFull operation. No warnings.
warningLess than 30 days until expiryFull operation. Dashboard shows a renewal warning.
graceExpired, within 30 days after expiryFull operation. Dashboard shows an urgent warning. Renew before grace ends.
expiredMore than 30 days past expiryThe proxy stops serving domains — internal_list_domains returns an empty list. No new connections are accepted.
Heartbeat

The proxy-core sends a heartbeat to proxyonprem.fipsign.dev/v1/heartbeat every 24 hours. The heartbeat includes the license_key and the current proxy version. FIPSign validates the license and responds with {"ok": true} if it is valid, or {"ok": false, "status": "revoked"} if it has been revoked.

ScenarioBehavior
Heartbeat OKNegative streak counter resets to 0. Normal operation continues.
3 consecutive negative heartbeatsProxy calls exit(1) — the container stops. This triggers a Docker restart if the restart policy is set (default: always). On the next start, the heartbeat is retried.
96 hours without a successful heartbeatProxy calls exit(1). This prevents indefinite operation with a firewalled heartbeat endpoint.
Network error or HTTP errorLogged as a warning. Does not increment the negative streak — only explicit ok: false responses do.
Do not firewall outbound HTTPS to proxyonprem.fipsign.dev. The proxy will stop after 96 hours without a successful heartbeat.
Re-verify the license from the dashboard

Go to Dashboard → License and click Re-verify. This re-reads the license.pqp file from disk and updates the verified_at timestamp in the database.

08 Alerts
Email alerts (requires SMTP) and webhook alerts (Slack, PagerDuty, or any HTTP endpoint).
Configure SMTP

SMTP is configured during installation. To add or change it later, run bash install.sh --reconfigure and answer Y to the SMTP question.

FieldExampleNotes
SMTP hostsmtp.resend.comAny SMTP provider — Resend, SendGrid, Postmark, Gmail, etc.
SMTP port587587 (STARTTLS), 465 (TLS), or 25.
UsernameresendSMTP authentication username.
PasswordStored in config.toml, not exposed via any API endpoint.
From addressPQ-Proxy <[email protected]>Sender address for alert emails.
TLS modestarttlsstarttls | tls | none
Alert triggers
EventChannelCondition
Certificate expiry warningEmail + WebhookCertificate expires in fewer than cert_expiry_warning_days (default: 30 days)
Certificate expiry criticalEmail + WebhookCertificate expires in fewer than cert_expiry_critical_days (default: 7 days)
License warningDashboard bannerLicense expires in fewer than 30 days
License graceDashboard bannerLicense expired — within the 30-day grace period
Webhook

Set a Webhook URL during installation. The payload is a JSON POST signed with HMAC-SHA256 using the webhook_secret from config.toml. Verify the signature in your handler with the X-Webhook-Signature header.

# Example webhook payload
{
  "event":   "cert_expiry_warning",
  "domain":  "api.yourcompany.com",
  "expires": "2026-07-25T10:00:00Z",
  "days":    5
}
09 Prometheus metrics
Enable the Prometheus endpoint to scrape connection metrics into Grafana or any compatible system.
Enable during installation or reconfiguration

Answer Y to Enable Prometheus endpoint? during the installer wizard. This sets prometheus_enabled = true in config.toml.

Scrape endpoint
http://YOUR_SERVER_IP:9090/metrics
Prometheus scrape config
# prometheus.yml
scrape_configs:
  - job_name: pqproxy-onprem
    static_configs:
      - targets: ['YOUR_SERVER_IP:9090']
Note: The metrics endpoint uses the same session-cookie authentication as the dashboard. For external Prometheus instances scraping from outside the browser, first obtain a session cookie via POST /api/v1/auth/token and pass it in the Cookie header of the scrape request.
10 Audit log
The audit log records all administrative actions — domain changes, certificate uploads, login events, and token rotations.
View in the dashboard

Go to Dashboard → Audit Log to browse recent events with timestamps, actor IP, and action details.

API — list audit log
GET /api/v1/audit-log
Response
{ "success": true, "data": [ { "id": "uuid", "action": "domain.create", "actor_ip": "181.47.8.234", "details": "api.yourcompany.com → 10.0.0.5:443", "created_at": "2026-07-25T10:00:00Z" } ] }
Export as CSV
GET /api/v1/audit-log/export

Downloads the full audit log as a CSV file. Useful for compliance and external auditing.

11 Connection metrics
Query connection logs and latency metrics for your domains.
Connection metrics
GET /api/v1/metrics/connections
Response
{ "success": true, "data": { "total_connections": 1024, "connections_last_24h": 87, "connections_last_7d": 612, "total_bytes_sent": 45231872, "total_bytes_received": 8192000, "by_domain": [ /* same fields per domain */ ] } }
Latency metrics
GET /api/v1/metrics/latency
Response
{ "success": true, "data": { "avg_tls_handshake_ms": 312.4, // X25519MLKEM768 handshake avg "avg_backend_connect_ms": 4.7, // time to reach your backend avg "avg_duration_ms": 89.2, "by_domain": [ /* same fields per domain */ ] } }
Export connection logs as CSV
GET /api/v1/connections/export

Downloads all connection logs within the retention window (configured at install time, default 30 days) as a CSV file. Each row is one TLS connection with fields: id, domain, peer_addr, bytes_sent, bytes_received, algorithm, connected_at, duration_ms, tls_handshake_ms, backend_connect_ms.

algorithm is always X25519MLKEM768 when the client supports post-quantum key exchange, or X25519 for classical TLS 1.3 fallback.
12 Updates
PQ-Proxy does not auto-update. You control when to apply new versions.
Check for available updates

If automatic update checks are enabled (default: every 24 hours), the dashboard shows a notification when a new version is available. You can also check manually:

GET /api/v1/updates/check
Response
{ "success": true, "data": { "current_version": "1.0.5", "latest_version": "1.0.6", "update_available": true, "check_enabled": true } }
Apply an update
# On your server, as root
docker compose pull && docker compose up -d

This pulls the latest api and proxy images from GHCR and restarts the containers. The database is not affected — all configuration and data is preserved. Downtime is typically under 5 seconds.

Note: The db container (PostgreSQL) is not updated by docker compose pull unless the image tag changes. Major PostgreSQL version upgrades require a separate migration procedure.
13 Reconfiguration
Change any configuration setting without reinstalling.
Run the reconfiguration wizard
curl -fsSL https://proxyonprem.fipsign.dev/install -o install.sh && bash install.sh --reconfigure

The wizard shows the current value of every setting as the default — press Enter to keep it, or type a new value. The database configuration is preserved automatically (cannot be changed via the wizard — edit config.toml manually if needed).

What reconfiguration preserves
PreservedNotes
INTERNAL_SERVICE_TOKENRead from .env — not regenerated. Changing it would break communication between containers.
Database URL and passwordRead from config.toml. The database is not touched.
All domains and certificatesStored in PostgreSQL — unaffected by reconfiguration.
What reconfiguration does NOT preserve
Not preservedNotes
Admin tokenThe token is stored in the database, not in config.toml — it is never regenerated by the wizard. Rotate it manually from the dashboard if needed.
Active sessionsRestarting the API container invalidates all in-memory session state. Users are redirected to the login page.
Alternative — edit config.toml directly
# Edit the configuration file
nano /root/config.toml

# Apply changes by restarting the API container
docker compose restart api

Changes to proxy settings (ports, real_ip_source, allow_private_backends) also require restarting the proxy container: docker compose restart api proxy.

14 Management API reference
All API endpoints exposed by the management-api on port 9090. Authentication required for all endpoints except /api/v1/auth/*.
Authentication

After logging in, the API sets a session cookie. All authenticated requests use this cookie automatically in the browser. For programmatic access, use the token login endpoint to obtain a session cookie, then include it in subsequent requests.

# Login with static token
curl -c cookies.txt -s \
  -X POST http://YOUR_SERVER_IP:9090/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"token": "pqp_onprem_..."}'

# Use the session cookie in subsequent requests
curl -b cookies.txt -s \
  http://YOUR_SERVER_IP:9090/api/v1/license
Endpoints
MethodPathDescription
POST/api/v1/auth/tokenLogin with static admin token. Body: {"token": "pqp_onprem_..."}
POST/api/v1/auth/otp/sendSend OTP to operator email. Requires SMTP. Body: {"email": "..."}
POST/api/v1/auth/otp/verifyVerify OTP and create session. Body: {"otp": "123456"}
POST/api/v1/auth/logoutInvalidate current session.
GET/api/v1/auth/meReturns current session info.
GET/api/v1/instanceReturns instance configuration (name, email, version).
GET/api/v1/licenseReturns license details and current status.
POST/api/v1/license/verifyRe-reads license.pqp from disk and updates the database.
GET/api/v1/domainsList all configured domains.
POST/api/v1/domainsAdd a domain. Body: {"domain","backend_host","backend_port","backend_tls"}
PUT/api/v1/domains/:idUpdate a domain's backend. Body: {"backend_host","backend_port","backend_tls"}
PATCH/api/v1/domains/:id/toggleEnable or disable a domain without deleting it.
DELETE/api/v1/domains/:idDelete a domain and its certificate.
GET/api/v1/certificatesList all certificates with source and expiry.
POST/api/v1/certificates/:domainUpload BYOC certificate. Body: {"cert_pem","key_pem"}
DELETE/api/v1/certificates/:domainRemove a BYOC certificate (reverts to ACME if enabled).
GET/api/v1/updates/checkCheck if a new version is available.
GET/api/v1/audit-logList audit log entries.
GET/api/v1/audit-log/exportDownload audit log as CSV.
GET/api/v1/metrics/connectionsConnection count and byte totals.
GET/api/v1/metrics/latencyTLS handshake and backend connect latency averages.
GET/api/v1/connections/exportDownload connection logs as CSV.
POST/api/v1/admin/rotate-tokenGenerate a new admin token. Requires active session.
POST/api/v1/admin/invalidate-sessionsInvalidate all active sessions. Requires active session.
Standard response envelope
{ "success": true | false, "data": // response payload, null on error, "error": // error message string, null on success }
15 FAQ
Common questions about PQ-Proxy On-Premise installation and operation.
Is the license verification 100% offline?

Yes. The license.pqp file contains a JSON payload signed with ML-DSA-65 (NIST FIPS 204). The proxy binary has the FIPSign public key embedded at compile time. Verification requires no network access — the signature is checked against the embedded key on every startup.

What happens if I lose the admin token?

If SMTP is configured, log in with an OTP and rotate the token from the dashboard. If SMTP is not configured and you have no active session, run this command on your server via SSH:

docker compose exec api pq-proxy-api reset-token

This generates a new token, saves it to the database, and prints it to the terminal. No manual database access required. The old token is immediately invalidated.

What happens during the grace period?

The proxy continues operating normally for 30 days after the license expires. All existing connections are served. You can add new domains. After the grace period ends, internal_list_domains returns an empty list — the proxy stops routing traffic until a valid license is installed.

How do I renew the license?

Purchase a new standard license at onprem.fipsign.dev/purchase. Download the new license.pqp file, copy it to the server at /root/license.pqp, and click Re-verify in the dashboard. No restart required — the new license is loaded immediately.

Can I run multiple instances with one license?

The license file does not enforce a specific server. However, each license is intended for use within a single organization. Running the same license on servers belonging to different organizations violates the terms of service and may result in revocation.

How do I completely uninstall?
# Stop and remove all containers and volumes
docker compose down -v

# Remove configuration files
rm -f /root/compose.yml /root/config.toml /root/.env /root/install.sh
Warning: docker compose down -v deletes the PostgreSQL volume and all data — domains, certificates, audit logs, and connection history. This is irreversible.