Security Overview

The DeployYourApp security model — trust boundaries, what we can and cannot see, tenant isolation, and the cryptography in use.


This page describes the security model in one place: where the trust boundaries sit, what DeployYourApp can read, how organizations are isolated from each other, and which algorithms are used where. Each area links to a page with the full detail.

Trust boundaries

There are four parties in an OTA update, and each holds different secrets.

Boundary Holds Never holds
Your machine / CI Signing private key, encryption public key, plaintext bundle
DeployYourApp server Signing public key, encryption public key, uploaded bundle bytes, wrapped session key Signing private key, encryption private key
Object storage (MinIO) Uploaded bundle bytes exactly as received Any key material
End-user device Encryption private key (compiled into your native binary), signing public key (plugin config) Signing private key

The signing private key never leaves the machine that generated it. dya keys generate writes it to disk with mode 0600 and uploads only the two public keys. See Code signing.

What DeployYourApp can and cannot see

Without bundle encryption, the server receives and stores your bundle as a plain ZIP. Our operators can read its contents.

With bundle encryption, the CLI encrypts the bundle before upload. The server stores ciphertext plus an RSA-OAEP-wrapped AES session key. The key that unwraps it is the encryption private key, which lives only in your app binary — the server never receives it. Under that configuration we cannot read bundle contents. See Bundle encryption.

Encryption is not on by default. It activates when dya-encryption-public.pem is present in the directory you run dya deploy from.

Separately, the SDKs report a random device UUID and a set of version strings on every update check, and send analytics events if analytics is left enabled. eventData on an analytics event is whatever your app puts in it, and we store it verbatim. SDK data collection lists every field.

Tenant isolation

Isolation is enforced at the request level, not by separate databases. All organizations share one PostgreSQL database and one MinIO bucket namespace.

  • Every organization-scoped route runs orgContext, which loads the org by the :orgId path parameter, rejects soft-deleted orgs, and looks up a member row for the calling user. No member row means 403.
  • An API key is bound to the organization it was created in. orgContext rejects it against any other org, even one its creator belongs to.
  • Permission checks run separately from membership checks. requirePermission resolves the member's role and tests it against the required permission string. API keys must additionally carry that permission in their scope list.
  • Bundle objects are stored under a path prefixed with the organization ID and app ID, and are served through presigned URLs valid for 1 hour (or through your CDN if BUNDLE_CDN_URL is set).
  • Superadmin platform routes require isSuperAdmin on the user record and are checked independently of organization membership.

The POST /api/update and POST /api/stats endpoints used by devices are unauthenticated by design. They are keyed by the globally unique app ID and are rate limited.

Cryptography summary

Every value below is what the shipped code does. Details on the linked pages.

Purpose Algorithm Parameters Details
Bundle signing RSASSA-PKCS1-v1_5 with SHA-256 RSA-4096 key, base64 signature Code signing
Bundle encryption AES-256-GCM 256-bit key, 96-bit nonce, 128-bit tag Encryption
Session key wrapping RSA-OAEP SHA-256 digest, MGF1-SHA-256, empty label, RSA-4096 key Encryption
Bundle integrity SHA-256 Hex-encoded, timing-safe comparison server-side Encryption
Password hashing scrypt N=16384, r=16, p=1, dkLen=64, 16-byte random salt Authentication
API key hashing SHA-256 Hex-encoded, plaintext never stored Authentication
Two-factor codes TOTP 6 digits, 30-second period Authentication
Build environment variables AES-256-GCM 256-bit key from FIELD_ENCRYPTION_KEY, 128-bit IV
Transport HTTPS/TLS HSTS max-age=31536000, includeSubDomains, preload

Server hardening

  • Security headers are set by @fastify/helmet: a content security policy with default-src 'self', HSTS as above, and Referrer-Policy: strict-origin-when-cross-origin.
  • CORS uses an explicit origin allow-list. There is no subdomain wildcard, so a compromised unrelated subdomain is never granted credentialed CORS. localhost is accepted only when NODE_ENV=development.
  • All request bodies are validated with Zod schemas at the route boundary. Fields not in the schema are stripped before any database write.
  • Database access goes through Prisma, which parameterises every query.
  • In production the error handler returns a generic INTERNAL_ERROR body; stack traces are logged, never returned.
  • Every endpoint is rate limited. See the table in Authentication.
  • Privileged actions are written to an audit log with actor, organization, IP address, user agent, and timestamp. The audit log is available on the Scale plan and above.

What we do not have

We hold no SOC 2, ISO 27001, PCI DSS, or HIPAA certification or attestation. We do not run a paid bug bounty. Do not represent otherwise to your own customers.

Report a vulnerability to info@deployyour.app. See Incident response for what happens next.


Previous
Team & Access
Next
Code Signing