Workload Identity · SPIFFE · Edge

svidmint

SPIFFE compatible workload identity for serverless and edge. Issues X.509 and JWT SVIDs to Lambda, Workers, GitHub Actions, and Deno via native attestation.

Released

View on GitHub ↗

In one line. svidmint proves who is running a workload, on Lambda, Cloudflare Workers, GitHub Actions, and Deno Deploy, by issuing SPIFFE-compatible identities using each platform's native attestation, so serverless code can authenticate to the rest of your infrastructure on equal terms with Kubernetes.

Every piece of software your services communicate with needs to answer two questions: what is it, and who is running it? The first question has a growing body of tooling, signed SBOMs, provenance attestations, keyless signatures. The second question has a standard answer for most cloud infrastructure: SPIFFE/SPIRE, the CNCF workload-identity framework used by service meshes, Kubernetes clusters, and long-lived VMs. But serverless and edge runtimes, the environments where a significant fraction of modern backend code now runs, cannot use it. svidmint closes that gap by substituting each platform's own attestation mechanism and issuing the same credential format SPIRE would produce.

Identity for code that has no node to attest fromBEFOREServerless functionNo portable identity,long-lived secretsshares a static keyvs.AFTERServerless functionSPIFFE SVID,short-lived, verifiablenative attestation Figure 1. Left: a Lambda function or Cloudflare Worker with no verifiable identity, authenticating to downstream services with static secrets or ambient IAM. Right: svidmint issues an X.509 or JWT SVID using the platform's own native attestation, giving the workload a cryptographic identity its peers can verify.


Terms in 30 seconds

Domain engineers: skip ahead, this is orientation for everyone else.

  • SPIFFE (Secure Production Identity Framework for Everyone): a CNCF standard that defines a uniform identity document format (the SVID) and URI scheme (spiffe://) for workloads, so services can authenticate each other without shared secrets. (spiffe.io)
  • SPIRE: the CNCF reference implementation of SPIFFE; runs a persistent agent on every node to do kernel and host-level attestation. (github.com/spiffe/spire)
  • SVID (SPIFFE Verifiable Identity Document): the credential SPIFFE issues, in two formats: X.509-SVID (a certificate carrying a SPIFFE URI in the SAN field) and JWT-SVID (a signed JWT carrying the same URI). Both can drive mTLS or bearer-token authentication.
  • Attestation: the process of verifying that a workload is what it claims to be, using evidence tied to the environment it runs in (kernel state, cloud provider metadata, OIDC tokens).
  • OIDC (OpenID Connect): a widely-deployed identity layer over OAuth 2.0 that GitHub Actions, Deno Deploy, and Cloudflare Workers all support for issuing short-lived signed tokens. (openid.net)
  • IETF WIMSE (Workload Identity in Multi-System Environments): an IETF working group defining interoperable workload identity for heterogeneous environments; architecture draft at -07 (March 2026), still Internet-Drafts / pre-RFC (status as of June 2026). (datatracker.ietf.org)

Why this matters

  • For platform and security engineers: static long-lived credentials (API keys, IAM access keys passed as environment variables) are the most common credential-theft vector in serverless deployments; a short-lived, cryptographically attested SVID eliminates the entire class, the same way Sigstore keyless signing eliminated long-lived signing keys.
  • For teams operating mixed infrastructure: the EU Cyber Resilience Act's reporting obligations begin 11 Sep 2026 and full applicability arrives 11 Dec 2027, and zero-trust network controls, including workload identity, are the accepted remediation pattern for the supply-chain attack classes CRA addresses; svidmint extends that control plane to the serverless workloads that SPIRE leaves unprotected (status as of June 2026).
  • For architects building service meshes: svidmint federates with existing SPIRE servers via trust-bundle exchange, so a Lambda function and a Kubernetes pod can establish mTLS to each other using the same identity substrate, without changing the mesh's policy model.

🧭 If you only read this far: svidmint gives serverless workloads the same cryptographic identity that Kubernetes workloads already have, using each platform's own attestation as evidence, so you can enforce zero-trust across your entire fleet rather than just the parts that can run a SPIRE agent.


The problem, for engineers who don't live in this space

Imagine a building where every employee has a photo ID badge, issued by a central office that checked their passport before printing it. Visitors scan their badge at every door and the policy is clear: no badge, no entry. Now imagine a contractor who works in the building every day but is not allowed to visit the ID office, their role has no permanent desk and they show up at a different entrance each time. They still need to open doors. The usual fix is to give them a laminated card with a number written in marker, a shared secret with no cryptographic binding to who they actually are. That is exactly the situation for most serverless workloads today.

Here is the concrete version. An AWS Lambda function that calls an internal gRPC service needs to prove it is an authorized function and not a spoofed caller. Without workload identity, the options are: an IAM role with overly broad permissions, a long-lived API key in an environment variable, or a shared secret rotated manually. None of these prove at call time that the specific function instance, in the specific account and region, with the specific ARN, is the one that was deployed intentionally. A SPIRE agent would do exactly that, but Lambda does not let you run a persistent process alongside the function; there is no kernel to interrogate, no node agent to query. The same is true on Cloudflare Workers, Deno Deploy, and GitHub Actions.

Why SPIRE can't issue identity on serverlessSPIRE agentpersistent node+ kernel attestneedsLambda / Workers /Deno, no nodenowhere to run a per-node agent on serverless or edge Figure 2. A serverless workload needs to call a downstream service. No SPIRE agent can run in the platform. Without svidmint, the workload falls back to static secrets with no cryptographic binding to the actual running identity (red gap).

Why this is genuinely hard. Each serverless platform has a different native attestation mechanism, AWS STS signed requests are nothing like Cloudflare Access JWTs, and mapping heterogeneous platform evidence to a uniform SPIFFE ID requires a policy layer that can express "this JWT with these claims is the same kind of thing as that STS assertion with those claims." The naive fix of wrapping everything in a cloud IAM role simply relocates the problem: IAM roles are not SPIFFE credentials and cannot participate in a SPIFFE-based trust domain directly.


What exists today (and the gap)

Tool / StandardIssues X.509 + JWT SVIDsServerless / edge attestationNative per-platform evidenceFederates with SPIRE
SPIRE (CNCF)✅❌❌✅
Cloud-native IAM (AWS, GCP, Azure)❌✅✅❌
Vault Agent (HashiCorp)⚠️❌❌❌
svidmint✅✅✅✅

No existing tool issues SPIFFE-compatible SVIDs on serverless and edge runtimes by consuming each platform's native attestation evidence and federating the result into a SPIRE trust domain. Cloud IAM knows the platforms natively but does not produce SPIFFE credentials. SPIRE produces SPIFFE credentials but cannot run on these platforms at all. svidmint occupies the intersection that neither alone reaches.


How it works

svidmint validates platform-specific attestation evidence, maps it to a SPIFFE ID via policy, and issues credentials. It has three parts:

  • Attestation Engine: validates platform-specific evidence from each of the four supported runtimes and extracts verified claims.
  • SPIFFE ID Mapper: translates the verified claims from attestation into a spiffe:// URI via configurable policy-based rules.
  • Certificate Authority: issues X.509 SVIDs and JWT SVIDs with lifecycle management, and handles trust-bundle exchange for federation.

Native platform evidence in, SPIFFE SVIDs outPlatformLambda · WorkersGH Actions · DenoAttestation EngineSPIFFE ID MapperCertificate AuthoritySTS / OIDC /Access JWTX.509 + JWT SVIDissueSPIRE federationtrust bundle Figure 3. An attestation request arrives from a serverless runtime, the Attestation Engine validates the platform evidence, the SPIFFE ID Mapper converts verified claims to a SPIFFE URI, and the CA issues the SVID. Trust bundles flow outward to SPIRE servers for federation.

Where trust is established, and what crossesCloud platform, trusted attesterWorkload (function)Native evidenceSTS / OIDC tokensvidmint trust domainAttestation Engine+ CAIssued SVIDverified crossing Figure 6. Trust boundaries (dashed): the platform trust boundary is crossed using each platform's native attestation token; svidmint's internal trust boundary separates raw claims from verified SPIFFE identities; the issued SVID crosses the federation boundary to a SPIRE trust domain. Green arrows carry verified, signed material; the red zone marks where unverified caller claims are rejected.

Attestation Engine

The engine accepts platform evidence over the HTTP API at /v1/attest and dispatches to one of four platform-specific validators. For AWS Lambda, it uses STS signed requests to extract the account ID, ARN, region, and function name, evidence that is cryptographically bound to the Lambda execution role and cannot be forged by a caller outside that account. For Cloudflare Workers, it validates a Cloudflare Access JWT. For GitHub Actions and Deno Deploy, it validates an OIDC token issued by the respective platform's OIDC provider. In every case the validator rejects any evidence it cannot fully verify before any claims leave the engine.

SPIFFE ID Mapper

Once attestation succeeds, the mapper applies policy-based rules to the verified claim set to produce a spiffe:// URI. A Lambda function with a verified ARN of arn:aws:lambda:us-east-1:123456789012:function:payment-worker might map to spiffe://example.org/aws/lambda/payment-worker depending on the configured policy. The mapping layer is explicit and inspectable, which means administrators can audit exactly which platform identities correspond to which SPIFFE principals without reverse-engineering the CA's issuance log.

Certificate Authority

The CA issues both X.509 SVIDs (with the SPIFFE URI in the Subject Alternative Name extension) and JWT SVIDs (with the SPIFFE URI as the sub claim), matching the credential formats the rest of a SPIFFE trust domain expects. Lifecycle management handles expiry and rotation. For federation, the CA participates in trust-bundle exchange with SPIRE servers, allowing a Kubernetes service that trusts a SPIRE root to also trust SVIDs issued by svidmint, enabling mTLS between a Lambda function and a Kubernetes pod with no changes to the Kubernetes-side policy.


The decisions that actually mattered

Native platform validators vs. a generic OIDC adapter

The fork. All four supported platforms can emit some form of OIDC-compatible token. The tempting shortcut is to treat all of them as generic OIDC, validate the JWT signature, and extract claims uniformly. Options. Generic OIDC-only adapter vs. per-platform validators that understand each platform's specific evidence format. Chosen. Per-platform validators. Trade-off accepted. More code per platform and a higher maintenance surface, adding a new runtime requires a new validator, not a configuration entry. The payoff is that AWS Lambda's STS-signed request path, which is not a JWT at all, can participate on equal terms, and per-platform validators can enforce platform-specific constraints (the specific STS claim structure, the Cloudflare Access audience) that a generic adapter would silently skip.

Trust domain per deployment vs. shared trust domain

The fork. Each svidmint deployment could operate its own isolated trust domain, or it could participate as a sub-CA in a broader SPIRE trust domain via federation. Options. Fully isolated issuance vs. federated trust-bundle exchange. Chosen. Federation as the primary integration path. Trade-off accepted. Setup requires configuring the trust-bundle exchange between svidmint and the upstream SPIRE server, more initial work than a standalone deployment. The benefit is that the serverless workloads gain full participation in an existing zero-trust mesh without requiring the mesh to be redesigned around a new trust root.


Why this is new, and why it's significant

What's novel. svidmint is, to my knowledge, the first tool to issue SPIFFE-compatible SVIDs on serverless and edge runtimes (AWS Lambda, Cloudflare Workers, GitHub Actions, Deno Deploy) by consuming each platform's native attestation mechanism and federating the result into a SPIRE trust domain, doing for serverless workloads what SPIRE does for nodes that can run a persistent agent.

Why it's significant to the field. Workload identity for heterogeneous environments is an explicitly named open problem in the security standards community. The IETF WIMSE working group (architecture draft -07, March 2026) exists precisely because service-to-service authentication across environments that span VMs, containers, and serverless runtimes lacks a uniform solution. WIMSE's architecture draft identifies the lack of a persistent node agent as the central challenge for serverless environments. svidmint is a concrete implementation of the pattern WIMSE is standardizing, not a conformant implementation of a finished RFC, since WIMSE remains pre-RFC as of June 2026, but a working system that anticipates and validates the architecture the working group is converging on. That positioning (early implementation of an emerging IETF standard) is a defensible significance claim independent of adoption numbers.

Standards and ecosystem alignment. This work implements and aligns with:

  • SPIFFE/SPIRE (CNCF): issues X.509-SVID and JWT-SVID in the formats the SPIFFE specification defines, and participates in trust-bundle exchange using SPIRE's federation protocol.
  • OIDC: validates OIDC tokens from GitHub Actions and Deno Deploy against their respective well-known discovery endpoints, following the standard token validation rules.
  • IETF WIMSE: the architecture svidmint implements (platform-native attestation, uniform SVID issuance, cross-domain federation) directly corresponds to the workload-identity pattern the WIMSE architecture Internet-Draft describes (draft -07, March 2026; pre-RFC as of June 2026).

📌 Honest scope. svidmint is validated against the four named platforms (AWS Lambda, Cloudflare Workers, GitHub Actions, Deno Deploy); it has not been independently adopted at scale, and WIMSE is still pre-RFC so this anticipates rather than conforms to a finished standard. The federation path requires a SPIRE server to exchange trust bundles with; purely standalone deployments work but do not gain cross-domain mTLS without that pairing.


See it run

# Homebrew
brew install sns45/tap/svidmint

# Initialize a trust domain
svidmint ca init --trust-domain example.org

# Register a workload entry with Lambda selectors
svidmint entry create \
  --spiffe-id spiffe://example.org/aws/lambda/payment-worker \
  --selector aws_lambda:account_id:123456789012 \
  --selector aws_lambda:function_name:payment-worker

# Start the server
svidmint server run --port 8443
INFO  svidmint starting               trust-domain=example.org port=8443
INFO  CA initialized                  root-cert-ttl=87600h
INFO  entry registered                spiffe-id=spiffe://example.org/aws/lambda/payment-worker
INFO  attestation engine ready        platforms=[aws_lambda cloudflare_workers github_actions deno_deploy]
INFO  /v1/attest ready                # ← attestation endpoint accepting platform evidence
INFO  /v1/bundle ready                # ← trust bundle endpoint for federation
INFO  /v1/validate ready              # ← SVID validation endpoint
INFO  /v1/health ready

Inside the Lambda function (Go SDK):

// svidmint Go SDK, implements standard SPIFFE WorkloadAPIClient interface
client, _ := svidmint.NewClient("https://svidmint.internal:8443")
svid, _ := client.FetchX509SVID(ctx)
// svid.ID == "spiffe://example.org/aws/lambda/payment-worker"  # ← cryptographic proof

🔬 Go deeper. The Attestation Engine dispatch table, the code that routes each platform's evidence to its validator, is the clearest entry point for understanding the per-platform contracts. See the repo at github.com/sns45/svidmint. ()


What's next

The immediate next step is coverage of additional serverless platforms, Vercel Functions and AWS Lambda SnapStart have distinct execution models worth specific attestation paths. svidmint is the second piece of a three-part arc: forgeseal proves what code is running by producing a signed, verifiable supply-chain record; svidmint proves who is running it by issuing a cryptographic workload identity; and assayward will decide whether to let it run by making admission decisions from both signals together. The larger goal is to make identity-bound, verifiable deployment a default rather than a capability reserved for teams running mature service meshes.


Appendix / references