Skip to content

refactor(server): tighten sandbox SPI — sealed VolumeMount, URL validation, worker auth stubs #1127

Description

@FelixTJDietrich

Part of #1097 (epic).

What ships

Four targeted SPI changes that close the gap between today's Docker-only sandbox surface and what a future Kubernetes adapter will need:

  1. SandboxSpec.volumeMounts is now a sealed List<VolumeMount> with HostPathMount, EmptyDirMount, ConfigMapMount, and SecretMount records — replacing today's Map<String,String>. InteractiveSandboxSpec gets the same change. The Docker adapter accepts only HostPathMount; the other three throw UnsupportedOperationException with a clear message naming the variant.

  2. NetworkPolicy validates URLs at construction. Non-absolute llmProxyUrl or gitProxyUrl is rejected (URI.create(url).isAbsolute()).

  3. WorkerAuthProvider SPI stub. Interface plus a RegistrationTokenAuthProvider reference implementation under de.tum.in.www1.hephaestus.worker.auth.spi. No worker bean wires this yet — that lands in the worker runtime epic — but the contract exists.

  4. WorkerJwtClaims record. {worker_id, workspace_id, exp, iat, jti}. Five fields, no scope[] — there's no third JWT-protected endpoint to scope-check against, so an unused field would be defense-in-depth theater.

Plus an ArchUnit rule that rejects literal ~/.hephaestus paths under agent/runtime/, agent/sandbox/, and worker/ packages — these subsystems should never assume a user home directory.

Why

These four are coupled by being SPI surface, not by being one logical feature. Splitting them creates four trivially small PRs that each touch the same SPI files and pessimize review attention. Keeping them in one PR keeps the SPI version bump (and the breaking change it represents to any downstream consumer of volumeMounts) in one place.

Acceptance criteria

  • SandboxSpec.volumeMounts: Map<String,String> replaced by List<VolumeMount> — a sealed interface with HostPathMount, EmptyDirMount, ConfigMapMount, SecretMount records
  • InteractiveSandboxSpec same refactor
  • DockerSandboxAdapter accepts only HostPathMount; the other variants throw UnsupportedOperationException with a message that names the unsupported variant
  • All call sites of volumeMounts(...) migrated (verified by grep showing zero remaining Map<String,String> callers)
  • NetworkPolicy constructor rejects non-absolute llmProxyUrl AND non-absolute gitProxyUrl; unit tests cover both
  • WorkerAuthProvider interface + RegistrationTokenAuthProvider reference implementation exist under de.tum.in.www1.hephaestus.worker.auth.spi
  • WorkerJwtClaims record with fields {worker_id, workspace_id, exp, iat, jti} — no scope[]
  • ArchUnit rule rejects the literal ~/.hephaestus under agent/runtime/, agent/sandbox/, and worker/ packages
  • No existing test regresses
  • OpenAPI codegen unaffected (these SPIs aren't exposed via REST)

Implementation notes

  • The volumeMounts change is the only breaking move in this PR. Audit downstream consumers (the integration sandbox adapter, the OpenCode adapter) and migrate them as part of the same PR — there should be no leftover Map<String,String> after merge.
  • The reference RegistrationTokenAuthProvider exists so the interface contract is exercised by a real implementation, not just a mock. Keep it minimal — it's a contract artifact, not a feature.
  • WorkerJwtClaims deliberately omits scope[]. Adding scope later if a third JWT-protected endpoint shows up is trivial; carrying an unused field today is the kind of defense-in-depth gesture that erodes trust in the rest of the surface.

Dependencies

Depends on #1124. Independent of #1125, #1126.

Metadata

Metadata

Assignees

No one assigned

    Labels

    application-serverSpring Boot server: APIs, business logic, databaserefactorCode restructuring without changing behaviorsecurityAuthentication, authorization, vulnerability fixes

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions