Figma — Enforcing Device Trust on Code Changes
Summary¶
Figma's security team adds a cryptographic device-trust check on every
Git commit that merges into release branches of its internal monorepo.
Every engineer's company-managed MacBook already holds a device-trust
X.509 certificate (rotated every 15 days via an existing separate
mechanism) in the macOS Keychain. Commits are signed with that cert via
S/MIME using a Figma-modified fork of GitHub's
smimesign utility
(smimesign-figma), invoked through a one-line wrapper bash script
that replaces Git's static user.signingkey argument with a dynamic
keychain lookup. On the verification side, a GitHub App +
AWS Lambda pair receives a GitHub webhook on every push, cryptographically
verifies the HEAD commit signature against Figma's internal CA, and
reports a required status check commit-integrity-verification back to
GitHub. PRs cannot merge into a release branch without this check
passing — so only code that originates on a trusted Figma MacBook can
reach production. The same verifier doubles as a bot-commit policy
enforcement point (e.g. allowlisting Dependabot, inspecting its diffs for
scope violations).
Key takeaways¶
-
Device trust, not user trust, is the cryptographic anchor. The X.509 key that signs each commit is bound to the laptop, not to the engineer's human identity. Enforcement answers "did this code come from a trusted Figma MacBook?" rather than "did this human authorise this commit?" (the latter remains handled by GitHub auth). See concepts/device-trust, patterns/signed-commit-as-device-attestation.
-
S/MIME via X.509 is the bridge to enterprise PKI. Git supports three signing families — GPG, SSH, and S/MIME (X.509). S/MIME is the only one that integrates natively with the X.509 certificates corporate CAs already issue. GitHub's smimesign utility exists precisely to plug enterprise X.509 into Git's pluggable-signer interface. See systems/smimesign, concepts/commit-signing.
-
Static
user.signingkeyis incompatible with short-lived rotating certs. Figma's device-trust certs rotate every 15 days; the standard Git signer interface passes a static<your_x509_key_id>configured once viagit config user.signingkey. Figma solves this by checking in a one-line wrapper script configured asgpg.x509.programthat ignores the caller-supplied args and invokessmimesign-figma --status-fd=2 -bsau smimesign-figma --get-figmate-key-id— i.e. computes the current key id from the keychain at commit time. See patterns/wrapper-script-arg-injection. Clever consequence: theuser.signingkeyconfig value is deliberately left blank and never participates in signing. -
Verification is stateless compute behind a webhook. A GitHub App (
Commit Signature Verification) registers a webhook on push events against the monorepo. The webhook targets an AWS Lambda Function URL; the Lambda authenticates as the GitHub App (credentials in AWS Secrets Manager), verifies the GitHub webhook secret, fetches commit metadata + signature via the GitHub API, cryptographically verifies the signature using the smimesign/ietf-cms library against the internal device-trust CA, and posts acommit-integrity-verificationcommit status back to GitHub. Branch-protection policies make this status a merge blocker. See patterns/webhook-triggered-verifier-lambda, systems/figma-commit-signature-verification. -
Bot commits are a first-class, separately-policed class. Bots (Dependabot, other externally-developed GitHub Apps) commit via the GitHub API and are signed with GitHub's web-flow GPG key, not any device-trust cert. The verifier routes these through an allowlist of trusted bot authors and can further inspect the diff — e.g. failing
commit-integrity-verificationon a Dependabot commit that touches non-dependency paths. This layers over the existing external-bot approval flows, reducing the risk of a compromised/malicious external App slipping a supply-chain edit. -
Least-privilege GitHub Apps are the authorised-integration primitive. The verifier is built as a GitHub App with only the permissions it needs: read code (to fetch the diff) and write commit status checks (to post the verification result). No broader repo/org write. Exemplar of the Figma-team principle "GitHub Apps to build secure, least-privileged tools that interact with the GitHub API." See systems/github-apps, concepts/least-privileged-access.
-
Default-by-construction security with near-zero engineer toil. Because signing is transparent (Git just signs every commit via the wrapper) and verification is automatic on push, engineers experience the control only as a status check — no extra steps, no added ceremony. The security team frames this as central to their philosophy: refusing the fork of "accept unnecessary risk" vs. "introduce tiresome processes", engineering their way into a safer default posture. "This minimization of toil is central to the philosophy of our team, and it frees us up to move quickly on new projects that make Figma more secure."
Mechanics¶
Git config the engineer ends up with¶
git config commit.gpgsign true
git config gpg.format x509
git config gpg.x509.program smimesign-figma-wrapper
# user.signingkey deliberately left blank
The wrapper¶
#!/usr/bin/env bash
# smimesign-figma-wrapper — IGNORES all args Git passes, substitutes its own.
smimesign-figma --status-fd=2 -bsau $(smimesign-figma --get-figmate-key-id)
(reconstructed — the blog shows the body without an explicit $(...); the
inner invocation does compute the current key id from the macOS keychain
and feed it to the outer -u flag at every commit.)
Verification flow¶
- Engineer pushes a signed commit to a feature branch.
- GitHub fires a push-event webhook to the Lambda Function URL.
- Lambda authenticates as the GitHub App (private key / app-id loaded from Secrets Manager).
- Lambda verifies the webhook secret signature (confirms the payload is from GitHub).
- Lambda fetches the HEAD commit's signature + changeset via the GitHub API.
- Lambda runs the
ietf-cmsverifier against the device-trust internal CA. - Lambda posts
commit-integrity-verification= pass/fail as a commit status. - Branch protection requires that status = pass to merge into release branches.
Bot path¶
- Bot commit → signed with GitHub web-flow GPG key → device-trust verification fails.
- Lambda checks author against an allowlist of trusted bots; allowlisted → can still pass the status check.
- Optional heuristic layer: inspect diff (e.g. Dependabot touching
non-
package-lock.jsonpaths → fail).
Architectural significance¶
- Canonical instance of binding supply-chain trust to device trust via existing enterprise PKI — no separate signing infra, no separate key lifecycle. The 15-day cert rotation already solves "what if a key is compromised" by making compromise windows short.
- Canonical instance of wrapper-script arg injection to bridge a tool's static-config assumption with a caller's dynamic-lookup need, without forking Git itself.
- Canonical instance of webhook-triggered verification as a Lambda GitHub App — a reusable shape for any "on every push, check X, post status" policy (supply-chain, license, secret scanning, custom gates).
- Demonstrates the GitHub-App-as-least-privilege-tool pattern the Figma security team explicitly cultivates.
Caveats / not covered¶
- No specifics on the internal CA issuing the 15-day device-trust certs. Figma's prior blog (referenced as "the same device trust certificates discussed before") presumably covers this; not in this article's scope. AWS Private CA vs. a different PKI cannot be inferred from the post.
- No discussion of how the MacBook enrolls and what happens when the
cert expires mid-commit (presumably the keychain has already rotated;
the
--get-figmate-key-idflag returns the current valid one). - No performance / latency numbers (Lambda cold-start effect on push latency not discussed). No cost / volume numbers.
- No breakdown of how the Lambda handles commits on feature branches that later get squashed / rebased on merge (the status attaches to HEAD).
- Linux/Windows engineers not mentioned —
smimesignsupports both macOS and Windows; Figma's--get-figmate-key-idflag is described only in terms of the macOS Keychain. - Key-revocation / CRL / OCSP semantics not detailed.
- The GitHub web-flow GPG key path for bots is named but the allowlist mechanics + diff-heuristics are sketched, not fully specified.