You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: client-side envelope encryption of binaries, flow zips & env vars (#94)
* feat: client-side envelope encryption of app binaries before upload (#1138)
Encrypt half of the dcd#1138 contract (the platform api + simulators are the
decrypt half). Opt-in via `--encrypt` or `DCD_ENCRYPT_BINARIES=1`; off by
default, so uploads are unchanged unless requested.
- src/utils/envelope.ts: per-upload DEK, chunked AES-256-GCM container
(streamed, constant memory), X25519 sealed-box DEK wrap. Byte-compatible with
api/src/common/crypto/envelope.ts (format in dcd/docs/binary-envelope-encryption.md).
- src/config/environments.ts: pinned per-env KEK public key slot (null until
provisioned; DCD_BINARY_KEK_PUBLIC override for testing).
- src/methods.ts: encrypt source in place before hashing/upload so the SHA,
dedup check, and both uploaders operate on ciphertext (binaries.sha =
ciphertext hash); attach the envelope to binaries.metadata.enc at finalise.
- src/types.ts: TAppMetadata.enc; binary.flags.ts: --encrypt; cloud/upload
thread the flag.
- test/unit/envelope.test.ts: round-trips (incl. the 15MB wikipedia.apk fixture)
through a decrypt mirroring the platform, tamper detection, and DEK wrap/unwrap.
Verified end-to-end: CLI-encrypted output + wrapped DEK decrypt byte-for-byte
with the real dcd api envelope code.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: pin dev and production KEK public keys for binary encryption
Fill in the previously-null kekPublicKey slots (version 1) now that the
key-encryption keypairs are provisioned. These are X25519 *public* keys and
are safe to embed in a public release, exactly like the Supabase anon keys
already checked in here; the matching private halves live only on the
platform API and are never shipped.
Update the resolver test to assert each environment resolves to its pinned
version and key rather than null.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: client-side encryption of flow zips and env vars
Extends the binary envelope scheme to the two remaining sensitive client-side
inputs — the Maestro flow zip and the injected --env KEY=VALUE secrets — so the
platform stores only ciphertext at rest. Enabling encryption now covers the
binary, the flow zip, and the env vars, each with its OWN per-upload DEK (all
wrapped under the same pinned per-environment KEK public key).
- utils/envelope.ts: encryptToContainer (in-memory DCDE twin of the streaming
file encryptor), encryptFlowBuffer, and encryptEnv (a single-segment inline
blob for results.env.enc), plus a shared isEncryptionEnabled(). Byte-compatible
with the platform decrypt half.
- test-submission.service.ts is the single seam: when encrypting, the flow zip
becomes a DCDE container (the sha sent to the API is the CIPHERTEXT hash), the
flow envelope rides fields.enc as a JSON string (so both the JSON and legacy
multipart submission paths carry it identically), and the env map becomes an
{ enc } envelope. Both the cloud command and the MCP tool get this for free
via buildTestPayload.
- --encrypt (and DCD_ENCRYPT=1) now gate all three; DCD_ENCRYPT_BINARIES=1 stays
as an alias for the binary-only behaviour.
- Round-trips flow + env ciphertext through a mirror of the platform decrypt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: allowlist the pinned KEK public keys in gitleaks
The two KEK public keys pinned in bfa2b4c tripped gitleaks' default
generic-api-key rule on entropy alone (5.02 / 4.89), failing the
secret-scan job. They are base64 of the raw 32-byte X25519 *public*
halves — encrypt-only, with the private halves living solely in API
env config, never in this repo.
Allowlisted by exact value, matching the convention already used for
the Supabase anon keys and deliberately not by file path or by the
whole rule: a KEK private key is byte-identical in shape to its public
half, so a path allowlist would blind the scanner to a genuine leak in
precisely the file most likely to contain one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: src/config/flags/binary.flags.ts
+5Lines changed: 5 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -24,4 +24,9 @@ export const binaryFlags = {
24
24
description:
25
25
'Ignore the sha hash check and upload the binary regardless of whether it already exists (not recommended)',
26
26
},
27
+
encrypt: {
28
+
type: 'boolean',
29
+
description:
30
+
'Encrypt the app binary, flow zip, and env vars before upload (client-side envelope encryption; each gets its own key). Can also be enabled with DCD_ENCRYPT=1.',
0 commit comments