Context and pod sugar¶
The expr-lang env Portal exposes to a rule has three layers (internal/api/context.go, internal/context/pod/builder.go):
object— universal. The raw resource as*unstructured.Unstructuredrendered as nested maps. Every field in the K8s schema is reachable:object.spec.replicas,object.spec.volumes[0].hostPath,object.spec.tolerations, custom CRD fields, anything. This is the escape hatch — no field is ever inaccessible.- Pod sugar — Pods and pod-shaped workloads only. A deliberately narrow façade matching
podwatcher-poc's surface byte-for-byte. request— admission only.operation,dryRun,userInfo.{username,uid,groups,extra},oldObject.
For non-pod GVKs only object, metadata, request are bound; the sugar keys are absent.
What the pod sugar covers¶
The pod ContextBuilder produces these top-level env keys for Pods and any GVK with a PodTemplateSpec (Deployment, StatefulSet, DaemonSet, ReplicaSet, Job, CronJob):
container.name
container.containerType # "standard" | "init" | "ephemeral"
container.image.registry # parsed from .image
container.image.name
container.image.tag
container.image.sha256
container.command
container.args
container.ports
container.securityContext.{privileged,allowPrivilegeEscalation,readOnlyRootFilesystem,
runAsUser,runAsGroup,runAsNonRoot,procMount,seccompProfileType,
capabilities.{add,drop}}
spec.hostPID
spec.hostNetwork
spec.hostIPC
spec.serviceAccountName
spec.automountServiceAccountToken
securityContext.{runAsUser,runAsGroup,runAsNonRoot,fsGroup,supplementalGroups,seccompProfileType}
metadata.{name,namespace,labels,annotations}
Anything outside this list — volumes, env, probes, lifecycle, nodeSelector, tolerations, affinity, topology spread constraints, priorityClassName, runtimeClassName, dns settings, restartPolicy, schedulerName, imagePullSecrets, hostAliases — is reachable via object.spec.<path>.
The sugar is not a typed mirror of v1.Pod. It grows additively when real rules demand it.
Multi-container iteration¶
The pod ContextBuilder's primary load-bearing job is fan-out: every rule is evaluated once per container with container rebound each pass. Standard, init, and ephemeral containers are all included; the iteration index is reflected in container.containerType.
A rule like container.securityContext.privileged == true fires per container — no foreach boilerplate. From raw object.spec.containers[] you would have to write the loop yourself.
Non-pod resources¶
For any GVK without a PodTemplateSpec the env is:
A rule against a ConfigMap or NetworkPolicy reaches its fields via object.data.<key> / object.spec.ingress[0].from. The container/spec/securityContext keys are bound to nil and any access through them must use the ?. operator.