Skip to main content

Security and signing

A printer on a shop counter should print only what the shop's own software sends. JodaPrint enforces this at several levels.

Request signing (default)​

Each organization creates one or more Ed25519 signing keys in the dashboard. The private key is shown once and stays on your backend; the public key goes to your agents with their configuration.

The browser SDK never holds a private key. For each request it calls your signer, which asks your backend to sign (see First print from the browser). The agent then checks:

  • the signature, over this string:

    v1
    {method}
    {orgId}
    {ts}
    {nonce}
    {sha256_hex(canonical_json(params))}

    Canonical JSON is RFC 8785 (JCS): keys sorted, no spaces. @jodaprint/node and @jodaprint/protocol implement it.

  • the timestamp: a signature older than 300 seconds is refused (SIGNATURE_EXPIRED);

  • the nonce: a nonce seen in the last 10 minutes is refused (NONCE_REUSED).

Your signing endpoint decides who may print: signRequestHandler requires an authorize callback, and can limit the methods it signs.

To rotate a key, create a new one, deploy it to your backend, then revoke the old one.

Allowed origins​

The agent refuses WebSocket connections from pages whose origin is not in the organization's list (ORIGIN_NOT_ALLOWED). With dev mode on, http://localhost:* is allowed too; turn it off in production.

Unsigned mode (optional)​

For small businesses without a backend, an organization can turn on unsigned mode. When an allowed origin sends an unsigned request, the tray asks the person at the computer: Allow, Block or Always allow. The choice is stored per site. It is off by default.

Local connection​

  • The agent listens only on 127.0.0.1 and ::1, on wss://localhost:8182 (then 8283, 8384, 8485).
  • At install, the agent creates a certificate authority for this machine only, limited to localhost names, and trusts it. The CA private key never leaves the machine; no certificate or key is shared between customers.

Devices and the cloud​

  • At activation each agent creates an Ed25519 device key; it proves its identity on each connection by signing a challenge from the cloud.
  • The agent always opens the connection (outbound). The cloud never connects into your network.
  • The agent only prints: no remote shell, no reading files outside its own folders, no running programs except its bundled PDF helper.
  • Updates are signed: the agent verifies each release with the JodaPrint release key embedded in it, and rolls back if a new version does not start.

Cloud​

  • API keys (jp_live_…, jp_test_…) are stored as SHA-256 hashes; only the first characters are kept to recognize them. Give each key only the scopes it needs.
  • Tenant isolation: every API key, device, printer and job belongs to exactly one organization, and every query filters by it.
  • Job content is encrypted at rest (AES-256-GCM) and deleted after the organization's retention period (default 7 days; 0, 1, 7, 30 or 90). Job metadata stays for the history.
  • Rate limits: by default 20 jobs per second per device and 100 requests per second per API key.
  • Webhooks are signed with HMAC-SHA256: see Webhooks.

Threat model and reporting​

The full threat model (a STRIDE table per component, with the tests that cover each threat) is docs/security.md in the JodaPrint repository. Report vulnerabilities privately to [email protected] (see SECURITY.md).