diff --git a/frontend/src/routes/dashboard/capsules/[id]/+page.svelte b/frontend/src/routes/dashboard/capsules/[id]/+page.svelte index 8b1aa67..a90d334 100644 --- a/frontend/src/routes/dashboard/capsules/[id]/+page.svelte +++ b/frontend/src/routes/dashboard/capsules/[id]/+page.svelte @@ -478,60 +478,8 @@ {:else if capsule}
- -
- {#if capsule.status === 'running'} - - {:else if capsule.status === 'paused'} - - - {/if} - - {#if capsule.status === 'running' || capsule.status === 'paused'} - - {/if} -
- - -
+ +
+ + +
+ {#if capsule.status === 'running'} + + {:else if capsule.status === 'paused'} + + + {/if} + + {#if capsule.status === 'running' || capsule.status === 'paused'} + + {/if} +
diff --git a/frontend/src/routes/dashboard/templates/+page.svelte b/frontend/src/routes/dashboard/templates/+page.svelte index b84a3d7..b852841 100644 --- a/frontend/src/routes/dashboard/templates/+page.svelte +++ b/frontend/src/routes/dashboard/templates/+page.svelte @@ -162,10 +162,10 @@ {/if} @@ -591,11 +591,15 @@ >
-

Launch Capsule

-

+ +

+ +
+

Launch Capsule

+

Configure resources and launch a new capsule from this snapshot.

@@ -612,12 +616,9 @@
{#if launchTarget.type === 'snapshot'} - + {:else} - + {/if} {launchTarget.name} @@ -694,7 +695,7 @@
+
{/if} @@ -715,17 +717,17 @@ .skeleton { background: linear-gradient( 90deg, - var(--color-bg-4) 0%, - var(--color-bg-5) 50%, - var(--color-bg-4) 100% + var(--color-bg-3) 25%, + var(--color-bg-4) 50%, + var(--color-bg-3) 75% ); background-size: 200% 100%; - animation: shimmer 1.6s ease-in-out infinite; + animation: shimmer 1.4s ease infinite; } @keyframes shimmer { - 0% { background-position: 200% center; } - 100% { background-position: -200% center; } + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } } /* Left accent stripe — slides in on hover, color-keyed to snapshot type */ @@ -745,4 +747,9 @@ .snapshot-row.type-image:hover { background: rgba(90, 159, 212, 0.04); } + + /* Empty state icon float — matches admin pattern */ + .empty-icon-float { + animation: iconFloat 3s ease-in-out infinite; + } diff --git a/images/wrenn-init.sh b/images/wrenn-init.sh index d83be1c..8a9e22e 100644 --- a/images/wrenn-init.sh +++ b/images/wrenn-init.sh @@ -17,8 +17,9 @@ mkdir -p /sys/fs/cgroup mount -t cgroup2 cgroup2 /sys/fs/cgroup 2>/dev/null || true echo "+cpu +memory +io" > /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null || true -# Set hostname +# Set hostname and make it resolvable (sudo requires this). hostname sandbox +echo "127.0.0.1 sandbox" >> /etc/hosts # Configure networking if the kernel ip= boot arg did not already set it up. if ! ip addr show eth0 2>/dev/null | grep -q "169.254.0.21"; then diff --git a/internal/service/build.go b/internal/service/build.go index 55defe6..c0aa8c6 100644 --- a/internal/service/build.go +++ b/internal/service/build.go @@ -420,8 +420,10 @@ func (s *BuildService) executeBuild(ctx context.Context, buildIDStr string) { } // Capture the final user and env vars as template defaults. + // Filter out user-specific and runtime vars that should be resolved at + // sandbox creation time, not baked in from the build environment. templateDefaultUser := bctx.User - templateDefaultEnv := bctx.EnvVars + templateDefaultEnv := filterBuildEnv(bctx.EnvVars) // Phase 3: Post-build (as root) — cleanup. bctx.User = "root" @@ -739,3 +741,27 @@ func (s *BuildService) uploadAndExtractArchive( return nil } + +// runtimeEnvVars lists env vars that are user- or session-specific and should +// not be persisted into template defaults. These are resolved at runtime by +// envd based on the actual user and sandbox context. +var runtimeEnvVars = map[string]bool{ + "HOME": true, "USER": true, "LOGNAME": true, "SHELL": true, + "PWD": true, "OLDPWD": true, "HOSTNAME": true, "TERM": true, + "SHLVL": true, "_": true, + // Per-sandbox identifiers set by envd at boot via MMDS. + "WRENN_SANDBOX_ID": true, "WRENN_TEMPLATE_ID": true, +} + +// filterBuildEnv returns a copy of envVars with runtime/user-specific +// variables removed so they don't override envd's per-user resolution. +func filterBuildEnv(envVars map[string]string) map[string]string { + filtered := make(map[string]string, len(envVars)) + for k, v := range envVars { + if runtimeEnvVars[k] { + continue + } + filtered[k] = v + } + return filtered +}