Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions cmd/atelet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -647,15 +647,15 @@ func (s *AteomHerder) prepareOCIBundles(
spec *ateletpb.WorkloadSpec,
targetAteomUid string,
) error {
// Populate the per-actor nameentity directory that gets bind-mounted into
// Populate the per-actor identity directory that gets bind-mounted into
// the application containers. Regenerated on every resume, so it carries
// the correct per-actor name even when restoring from the golden snapshot.
identityDir := ateompath.ActorIdentityDirPath(atespace, actorName)
if err := os.MkdirAll(identityDir, 0o755); err != nil {
return fmt.Errorf("while creating actor nameentity dir: %w", err)
return fmt.Errorf("while creating actor identity dir: %w", err)
}
if err := writeFileAtomic(filepath.Join(identityDir, ActorIDFileName), []byte(actorName), 0o644); err != nil {
return fmt.Errorf("while writing actor nameentity file: %w", err)
if err := writeFileAtomic(filepath.Join(identityDir, ActorNameFileName), []byte(actorName), 0o644); err != nil {

@zoez7 Zoe Zhao (zoez7) Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Luiz Oliveira (@laoj2) , #438 is updating this to use the Actor ID, because we want to avoid actors with the same names accidentally sharing this directory.

return fmt.Errorf("while writing actor identity file: %w", err)
}

ddVolumes := make(map[string]bool)
Expand Down Expand Up @@ -698,7 +698,7 @@ func (s *AteomHerder) prepareOCIBundles(
nil,
annotations,
ateompath.AteomNetNSPath(targetAteomUid),
"", // pause is sandbox infra; it gets no actor nameentity mount.
"", // pause is sandbox infra; it gets no actor identity mount.
nil,
); err != nil {
return wrapFileSystemErr("while creating pause OCI bundle", err)
Expand Down Expand Up @@ -1044,10 +1044,10 @@ func resetActorDirs(atespace, actorName string) error {
// reads it through the gofer.
identityDir := ateompath.ActorIdentityDirPath(atespace, actorName)
if err := os.RemoveAll(identityDir); err != nil {
return wrapFileSystemErr("while deleting actor nameentity dir: %w", err)
return wrapFileSystemErr("while deleting actor identity dir: %w", err)
}
if err := os.MkdirAll(identityDir, 0o755); err != nil {
return wrapFileSystemErr("while creating actor nameentity dir: %w", err)
return wrapFileSystemErr("while creating actor identity dir: %w", err)
}

durableDirVolumesMountDir := ateompath.DurableDirVolumeMountsDir(atespace, actorName)
Expand Down
2 changes: 1 addition & 1 deletion cmd/atelet/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import (

func TestWriteFileAtomic(t *testing.T) {
dir := t.TempDir()
target := filepath.Join(dir, "actor-id")
target := filepath.Join(dir, "actor-name")

// One shared write over an existing value, as happens on every resume;
// each subtest checks one postcondition.
Expand Down
10 changes: 5 additions & 5 deletions cmd/atelet/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ const (
// changing the mount shape.
IdentityMountPath = "/run/ate"

// ActorIDFileName is the file inside IdentityMountPath holding the
// actor's own ID, raw with no trailing newline.
ActorIDFileName = "actor-id"
// ActorNameFileName is the file inside IdentityMountPath holding the
// actor's own name, raw with no trailing newline.
ActorNameFileName = "actor-name"
)

func prepareOCIDirectory(ctx context.Context, pullCache *memorypullcache.MemoryPullCache, atespace, actorName, containerName, ref string, args []string, env []string, annotations map[string]string, netns string, identityDir string, durableDirVolumeMounts []*ateletpb.VolumeMount) error {
Expand Down Expand Up @@ -82,8 +82,8 @@ func prepareOCIDirectory(ctx context.Context, pullCache *memorypullcache.MemoryP
return fmt.Errorf("in untar: %w", err)
}

// Bind-mount the per-actor nameentity directory so the workload can read its
// own ID at IdentityMountPath/ActorIDFileName. The bind target must exist
// Bind-mount the per-actor identity directory so the workload can read its
// own name at IdentityMountPath/ActorNameFileName. The bind target must exist
// in the rootfs for the mount to attach.
if identityDir != "" {
if err := createMountPoint(rootPath, IdentityMountPath); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/atelet/oci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestBuildActorOCISpec_IdentityMount(t *testing.T) {
}
found = true
if m.Source != "/host/actors/atespace:id/identity" {
t.Errorf("identity mount source = %q, want the per-actor nameentity dir", m.Source)
t.Errorf("identity mount source = %q, want the per-actor identity dir", m.Source)
}
if m.Type != "bind" {
t.Errorf("identity mount type = %q, want bind", m.Type)
Expand Down
2 changes: 1 addition & 1 deletion cmd/ateom-microvm/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func ensureKataCompatibleSpec(bundle, id, netnsPath string) (*specs.Spec, error)
// agent accepts. (Static shaper; pod DNS integration is future work.)
//
// KNOWN GAP vs the gVisor runtime: this also drops atelet's read-only actor
// identity bind mount (/run/ate/actor-id). The micro-VM guest can't see host
// identity bind mount (/run/ate/actor-name). The micro-VM guest can't see host
// paths (the rootfs is an overlay of a virtio-fs base + a guest-RAM upper, not a
// host bind), so atelet's host-path identity mount has nothing to bind to.
// Exposing the identity needs a per-actor volume plumbed into the guest; not yet
Expand Down
8 changes: 4 additions & 4 deletions docs/api-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ Because a snapshot is not restorable across sandbox runtimes, `sandboxClass` is
Container environment variables support literal `value` entries and `valueFrom.secretKeyRef`. Secret references are resolved by `ate-api-server` from the `ActorTemplate` namespace when a workload spec is materialized. For the golden actor, the resolved values are captured in the golden snapshot and future actors inherit those values until the golden snapshot is recreated. For an actor that bypasses the golden snapshot and boots from the current template spec, the resolved values are sent to atelet but are not serialized into the public Actor API. Other Kubernetes `valueFrom` sources are not supported yet. Secret changes do not automatically restart actors or invalidate snapshots; rotating a Secret requires an explicit actor or template lifecycle action.

### Workload Connectivity (Uniform DNS)
Substrate uses a **Uniform DNS Mesh**: every actor created from a template is automatically reachable through the **Substrate Router** via its atespace and ID:
Substrate uses a **Uniform DNS Mesh**: every actor created from a template is automatically reachable through the **Substrate Router** via its atespace and name:

**Format:** `<actor-id>.<atespace>.actors.resources.substrate.ate.dev`
**Format:** `<actor-name>.<atespace>.actors.resources.substrate.ate.dev`

### Actor Identity
Substrate bind-mounts a read-only, per-actor identity directory at **`/run/ate`** into each of the actor's containers. An actor can learn its own ID without parsing the `Host` header by reading the file **`/run/ate/actor-id`** inside it, which contains the raw actor ID with no trailing newline. Further identity and configuration data may appear in this directory over time.
Substrate bind-mounts a read-only, per-actor identity directory at **`/run/ate`** into each of the actor's containers. An actor can learn its own name without parsing the `Host` header by reading the file **`/run/ate/actor-name`** inside it, which contains the raw actor name with no trailing newline. Further identity and configuration data may appear in this directory over time.

Read it fresh rather than caching it at process start. It is delivered as a per-actor bind mount, not an environment variable, precisely so it carries the correct ID after a resume from the golden snapshot — an env var (or a file baked into the image) would be frozen at the *golden* actor's ID, since it lives in the checkpointed process memory, and would therefore be identical for every actor of the template.
Read it fresh rather than caching it at process start. It is delivered as a per-actor bind mount, not an environment variable, precisely so it carries the correct name after a resume from the golden snapshot — an env var (or a file baked into the image) would be frozen at the *golden* actor's name, since it lives in the checkpointed process memory, and would therefore be identical for every actor of the template.

### Container Readiness Probe (`readyz`)

Expand Down
2 changes: 1 addition & 1 deletion internal/ateompath/ateompath.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func ActorPath(atespace, actorName string) string {
}

// ActorIdentityDirPath is the host directory atelet populates with the
// actor's identity data (currently the single file "actor-id") and
// actor's identity data (currently the single file "actor-name") and
// bind-mounts read-only into the actor. It is per-actor and regenerated on
// every resume, so (unlike the checkpointed process environment) it reflects
// the correct ID after a restore from the golden snapshot.
Expand Down
4 changes: 2 additions & 2 deletions internal/e2e/fixtures/probe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import (
"os"
)

// identityFile is the actor-id file inside the identity directory atelet
// identityFile is the actor-name file inside the identity directory atelet
// bind-mounts at IdentityMountPath.
const identityFile = "/run/ate/actor-id"
const identityFile = "/run/ate/actor-name"

// whoami reports the actor's identity as observed at request time from the
// bind-mounted identity file. A read failure is reported in the response
Expand Down
2 changes: 1 addition & 1 deletion internal/e2e/suites/identity/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func TestActorIdentity_AfterRestore_IsOwnID_NotGolden(t *testing.T) {
got := whoami(t, ctx, rc, id)

if got.File != id {
t.Errorf("actor %q: /run/ate/actor-id = %q, want %q (probe read error: %q)", id, got.File, id, got.Error)
t.Errorf("actor %q: /run/ate/actor-name = %q, want %q (probe read error: %q)", id, got.File, id, got.Error)
}
if got.File == golden {
t.Errorf("actor %q: identity is the GOLDEN snapshot id %q — restore leaked shared state", id, golden)
Expand Down
Loading