Realm Support
By default, serverless attestation policies are defined at the trust-domain scope and the SPIFFE IDs have no realm prefix. With realm support, a serverless policy can be scoped to a Realm, so its SPIFFE IDs are namespaced under the realm (spiffe://<trust-domain>/<realm>/…) and the policy can be delegated to a realm administrator — the same realm model already used for agent-based clusters.
Realm support is optional and backward-compatible: existing trust-domain-scoped policies continue to work unchanged.
Trust-domain scoped vs. realm-enabled
| Trust-domain scoped (default, unchanged) | Realm-enabled | |
|---|---|---|
| Where the policy lives | Trust-domain config | A serverless cluster's config |
| Applied with | config set trust-domain | config set cluster |
| SPIFFE ID | spiffe://<td>/… | spiffe://<td>/<realm>/… |
| Managed by | Organization administrators | Realm administrators (or org admins) |
| Workload sends | DEFAKTO_TRUST_DOMAIN_ID | DEFAKTO_TRUST_DOMAIN_ID + DEFAKTO_CLUSTER_ID |
How it works
A realm-enabled serverless workload is represented by a cluster with the serverless platform type, optionally assigned to a realm. A "cluster" here is simply a named, realm-aware configuration container. There is no agent and no Kubernetes involved. The workload tells the Trust Domain Server which cluster configuration to use by supplying the cluster ID. The server evaluates that cluster's serverless policies and derives the realm from the cluster.
The walkthrough below uses AWS Web Identity Token, but the same steps apply to any other serverless proofs.
The realm must already exist before a cluster can be assigned to it. To create one:
spirlctl trust-domain realm create payments --trust-domain <trust-domain-name>
Skip this if the realm already exists, or if you are creating a serverless cluster with no realm.
Step 1 — Create a serverless cluster
Create a cluster with the serverless platform type and assign it to a realm:
spirlctl cluster add lambda-payments \
--trust-domain <trust-domain-name> \
--platform serverless \
--realm payments
Note the new cluster's ID (e.g. c-9f2k7r0a3b) which will be used in the next steps.
Step 2 — Configure the serverless policy on the cluster
This is the same ServerlessAttestation document as a trust-domain-scoped policy, with two differences: It is applied to a cluster instead of the trust domain, and its pathTemplate must begin with the realm.
section: ServerlessAttestation
schema: v1
spec:
policies:
- name: aws_policy
svidPolicy:
pathTemplate: "/{{realm.name}}/aws/{{aws_token.account.id}}"
requiredAttestors:
- type: aws_token
config:
issuerURLs:
- "https://a1e777e5-1234-5678-9bf8-cdda2afef4bb.tokens.sts.global.api.aws"
# Optional attestor filter. Unrelated to the realm — see
# "Separating multiple realms in one account" below.
allowedPrincipalTags:
environment:
- production
On a cluster assigned to a realm, the pathTemplate must start with /{{realm.name}}. The template renders it like any other attribute, so a template that omits /{{realm.name}} is rejected when the configuration is applied.
A trust-domain-scoped serverless policy has no realm and must not include /{{realm.name}}.
{{realm.name}} works only in pathTemplate{{realm.name}} is available to the SPIFFE ID pathTemplate only. It is not
available in an x509.subject template or in JWT additionalClaims. A policy
using it there is accepted when the configuration is applied, but SVID issuance
then fails with InvalidArgument. Use the other verified attributes in those
templates.
Apply it to the cluster (note config set cluster, not config set trust-domain):
spirlctl config set cluster --id <cluster-id> serverless.yaml
Or using Terraform:
resource "spirl_cluster_config" "serverless_attestation" {
cluster_id = spirl_cluster.lambda_payments.id
sections = {
ServerlessAttestation = <<-YAML
section: ServerlessAttestation
schema: v1
spec:
policies:
- name: aws_policy
svidPolicy:
pathTemplate: "/{{realm.name}}/aws/{{aws_token.account.id}}"
requiredAttestors:
- type: aws_token
config:
issuerURLs:
- "https://a1e777e5-1234-5678-9bf8-cdda2afef4bb.tokens.sts.global.api.aws"
# Optional attestor filter. Unrelated to the realm — see
# "Separating multiple realms in one account" below.
allowedPrincipalTags:
environment:
- production
YAML
}
}
The cluster supplies the realm name, and {{realm.name}} in the template renders it. With Trust Domain example.org and the cluster in the payments realm, the example above yields:
spiffe://example.org/payments/aws/<aws-account-id>
Step 3 — Point the workload at the cluster
The workload supplies its cluster ID alongside the trust-domain ID. With the SDK, set the DEFAKTO_CLUSTER_ID environment variable (see SDK Examples):
DEFAKTO_TRUST_DOMAIN_ID=<trust-domain-id>
DEFAKTO_CLUSTER_ID=<cluster-id>
DEFAKTO_ATTESTORS=aws_external_identity
If the workload omits DEFAKTO_CLUSTER_ID it will only match trust-domain-scoped policies.
Separating multiple realms in one account
Any workload that can present a token matching the policy can obtain an SVID, therefore realm separation is enforced by the policy's attestor filters, not by the cluster ID alone. To keep workloads in one cloud account from obtaining another realm's SVIDs, constrain each realm's policy to an attribute that the cloud provider controls and the workload cannot forge:
-
AWS Web Identity Token — gate on an IAM-assigned principal tag using
allowedPrincipalTags, so only roles carrying that tag satisfy the policy. Tagging execution roles with the realm they belong to is one way to do this:allowedPrincipalTags:realm:- paymentsThe tag key is arbitrary. Using
realmis just a convention, and the value is matched literally. The tag is not connected to the cluster's realm assignment. -
GCP Instance Identity Token — gate on the workload's service account with
allowedServiceAccounts, so only workloads running as that service account satisfy the policy:allowedServiceAccounts:- payments@my-project.iam.gserviceaccount.com -
Azure Managed Identity — gate on the managed identity's
principalID, the Entra object ID of the identity the workload runs under:tenants:- tenantID: 00000000-0000-0000-0000-000000000000principalID: 11111111-1111-1111-1111-111111111111
The Trust Domain Server enforces whatever a policy requires, but it cannot by itself know which workload "belongs" to which realm. The mapping from workload to realm depends on how you configure your cloud IAM and your policies. Define each realm's attestor filters so they are mutually exclusive. This is the same trust model as today's serverless API (any workload that satisfies a policy can obtain its SVID). Realm filters let you lock it down further.
Who can manage realm-enabled policies
A serverless cluster assigned to a realm is managed like any other cluster in that realm: realm administrators can configure it (via config set cluster), scoped to their realm only. A serverless cluster with no realm is managed by organization administrators — the same as trust-domain-scoped serverless policies today.