From 8e5e5d6199f991e79ff3b2d63eba9652a60242c8 Mon Sep 17 00:00:00 2001 From: sjmiller609 <7516283+sjmiller609@users.noreply.github.com> Date: Thu, 19 Mar 2026 19:35:17 +0000 Subject: [PATCH 1/2] feat: support updating egress proxy secret envs on running instances Adds PATCH /instances/{id}/env endpoint that merges new env vars into a running instance and re-registers egress proxy header-injection rules. This enables credential rotation without instance restart. Design: - New env vars are merged into existing env (not replaced) - validateCredentialEnvBindings() ensures all credential bindings are still satisfied after the update - maybeRegisterEgressProxy() atomically swaps proxy rules via the existing RegisterInstance() idempotent re-registration path - Updated metadata is persisted to disk - No guest-side changes needed since secrets never enter the guest Files changed: - openapi.yaml: PATCH /instances/{id}/env endpoint definition - lib/oapi/oapi.go: regenerated from OpenAPI spec - lib/instances/types.go: UpdateInstanceEnvRequest type - lib/instances/manager.go: UpdateInstanceEnv interface method - lib/instances/update_env.go: implementation - cmd/api/api/instances.go: HTTP handler - lib/scopes/scopes.go: route scope mapping - lib/instances/egress_proxy_integration_test.go: secret rotation test Co-Authored-By: Claude Opus 4.6 --- cmd/api/api/instances.go | 50 ++ .../egress_proxy_integration_test.go | 16 + lib/instances/manager.go | 3 + lib/instances/types.go | 5 + lib/instances/update_env.go | 86 ++ lib/oapi/oapi.go | 747 +++++++++++++----- lib/scopes/scopes.go | 1 + openapi.yaml | 64 ++ 8 files changed, 761 insertions(+), 211 deletions(-) create mode 100644 lib/instances/update_env.go diff --git a/cmd/api/api/instances.go b/cmd/api/api/instances.go index ba5b448a..0cc877e4 100644 --- a/cmd/api/api/instances.go +++ b/cmd/api/api/instances.go @@ -637,6 +637,56 @@ func (s *ApiService) StartInstance(ctx context.Context, request oapi.StartInstan return oapi.StartInstance200JSONResponse(instanceToOAPI(*result)), nil } +// UpdateInstanceEnv updates environment variables on a running instance and refreshes +// egress proxy rules. Enables credential rotation without instance restart. +func (s *ApiService) UpdateInstanceEnv(ctx context.Context, request oapi.UpdateInstanceEnvRequestObject) (oapi.UpdateInstanceEnvResponseObject, error) { + inst := mw.GetResolvedInstance[instances.Instance](ctx) + if inst == nil { + return oapi.UpdateInstanceEnv500JSONResponse{ + Code: "internal_error", + Message: "resource not resolved", + }, nil + } + log := logger.FromContext(ctx) + + if request.Body == nil || request.Body.Env == nil { + return oapi.UpdateInstanceEnv400JSONResponse{ + Code: "invalid_request", + Message: "request body with env is required", + }, nil + } + + result, err := s.InstanceManager.UpdateInstanceEnv(ctx, inst.Id, instances.UpdateInstanceEnvRequest{ + Env: request.Body.Env, + }) + if err != nil { + switch { + case errors.Is(err, instances.ErrInvalidState): + return oapi.UpdateInstanceEnv409JSONResponse{ + Code: "invalid_state", + Message: err.Error(), + }, nil + case errors.Is(err, instances.ErrInvalidRequest): + return oapi.UpdateInstanceEnv400JSONResponse{ + Code: "invalid_request", + Message: err.Error(), + }, nil + case errors.Is(err, instances.ErrNotFound): + return oapi.UpdateInstanceEnv404JSONResponse{ + Code: "not_found", + Message: "instance not found", + }, nil + default: + log.ErrorContext(ctx, "failed to update instance env", "error", err) + return oapi.UpdateInstanceEnv500JSONResponse{ + Code: "internal_error", + Message: "failed to update instance env", + }, nil + } + } + return oapi.UpdateInstanceEnv200JSONResponse(instanceToOAPI(*result)), nil +} + // logsStreamResponse implements oapi.GetInstanceLogsResponseObject with proper SSE flushing type logsStreamResponse struct { logChan <-chan string diff --git a/lib/instances/egress_proxy_integration_test.go b/lib/instances/egress_proxy_integration_test.go index 96fdd86c..7d0473b5 100644 --- a/lib/instances/egress_proxy_integration_test.go +++ b/lib/instances/egress_proxy_integration_test.go @@ -128,6 +128,22 @@ func TestEgressProxyRewritesHTTPSHeaders(t *testing.T) { require.Equal(t, 0, blockedExitCode, "curl output: %s", blockedOutput) require.Equal(t, "", blockedOutput) + // --- Secret rotation: update credential env and verify proxy injects new value --- + rotatedInst, err := manager.UpdateInstanceEnv(ctx, inst.Id, UpdateInstanceEnvRequest{ + Env: map[string]string{ + "OUTBOUND_OPENAI_KEY": "rotated-openai-key-456", + }, + }) + require.NoError(t, err) + require.Equal(t, StateRunning, rotatedInst.State) + + // Verify the proxy now injects the rotated credential + rotatedOutput, rotatedExitCode, err := execCommand(ctx, inst, "sh", "-lc", allowedCmd) + require.NoError(t, err) + require.Equal(t, 0, rotatedExitCode, "curl output: %s", rotatedOutput) + require.Contains(t, rotatedOutput, "Bearer rotated-openai-key-456") + require.NotContains(t, rotatedOutput, "real-openai-key-123") + require.NoError(t, manager.DeleteInstance(ctx, inst.Id)) deleted = true } diff --git a/lib/instances/manager.go b/lib/instances/manager.go index 8305a0c5..47ebcd93 100644 --- a/lib/instances/manager.go +++ b/lib/instances/manager.go @@ -39,6 +39,9 @@ type Manager interface { RestoreSnapshot(ctx context.Context, id string, snapshotID string, req RestoreSnapshotRequest) (*Instance, error) StopInstance(ctx context.Context, id string) (*Instance, error) StartInstance(ctx context.Context, id string, req StartInstanceRequest) (*Instance, error) + // UpdateInstanceEnv merges new env vars into a running instance and re-registers + // egress proxy rules so that rotated credentials take effect immediately. + UpdateInstanceEnv(ctx context.Context, id string, req UpdateInstanceEnvRequest) (*Instance, error) StreamInstanceLogs(ctx context.Context, id string, tail int, follow bool, source LogSource) (<-chan string, error) RotateLogs(ctx context.Context, maxBytes int64, maxFiles int) error AttachVolume(ctx context.Context, id string, volumeId string, req AttachVolumeRequest) (*Instance, error) diff --git a/lib/instances/types.go b/lib/instances/types.go index 8dc48837..deb7c75b 100644 --- a/lib/instances/types.go +++ b/lib/instances/types.go @@ -267,6 +267,11 @@ type ForkSnapshotRequest struct { TargetHypervisor hypervisor.Type // Optional, allowed only for Stopped snapshots } +// UpdateInstanceEnvRequest is the domain request for updating env vars on a running instance. +type UpdateInstanceEnvRequest struct { + Env map[string]string // Env vars to merge into the existing env +} + // AttachVolumeRequest is the domain request for attaching a volume (used for API compatibility) type AttachVolumeRequest struct { MountPath string diff --git a/lib/instances/update_env.go b/lib/instances/update_env.go new file mode 100644 index 00000000..b67c0c57 --- /dev/null +++ b/lib/instances/update_env.go @@ -0,0 +1,86 @@ +package instances + +import ( + "context" + "fmt" + + "github.com/kernel/hypeman/lib/network" +) + +// UpdateInstanceEnv updates environment variables on a running instance and +// re-registers egress proxy header-injection rules with the new values. +// This enables credential rotation without restarting the VM. +func (m *manager) UpdateInstanceEnv(ctx context.Context, id string, req UpdateInstanceEnvRequest) (*Instance, error) { + lock := m.getInstanceLock(id) + lock.Lock() + defer lock.Unlock() + return m.updateInstanceEnv(ctx, id, req) +} + +func (m *manager) updateInstanceEnv(ctx context.Context, id string, req UpdateInstanceEnvRequest) (*Instance, error) { + inst, err := m.getInstance(ctx, id) + if err != nil { + return nil, ErrNotFound + } + + if inst.State != StateRunning && inst.State != StateInitializing { + return nil, fmt.Errorf("%w: instance must be running (current state: %s)", ErrInvalidState, inst.State) + } + + if inst.NetworkEgress == nil || !inst.NetworkEgress.Enabled { + return nil, fmt.Errorf("%w: instance does not have egress proxy enabled", ErrInvalidRequest) + } + + if len(inst.Credentials) == 0 { + return nil, fmt.Errorf("%w: instance has no credential policies configured", ErrInvalidRequest) + } + + // Load persisted metadata so we can update and save it + meta, err := m.loadMetadata(id) + if err != nil { + return nil, fmt.Errorf("load metadata: %w", err) + } + + if meta.Env == nil { + meta.Env = make(map[string]string) + } + for k, v := range req.Env { + meta.Env[k] = v + } + + // Validate that all credential bindings are still satisfied + if err := validateCredentialEnvBindings(meta.Credentials, meta.Env); err != nil { + return nil, err + } + + // Get the network allocation for this running instance so we can + // re-register the proxy with the correct source IP / TAP / gateway. + alloc, err := m.networkManager.GetAllocation(ctx, id) + if err != nil { + return nil, fmt.Errorf("get network allocation: %w", err) + } + if alloc == nil { + return nil, fmt.Errorf("no network allocation found for running instance") + } + netConfig := &network.NetworkConfig{ + IP: alloc.IP, + MAC: alloc.MAC, + Gateway: alloc.Gateway, + Netmask: alloc.Netmask, + DNS: alloc.DNS, + TAPDevice: alloc.TAPDevice, + } + + // Re-register egress proxy with updated inject rules (atomically swaps old rules). + if _, err := m.maybeRegisterEgressProxy(ctx, &meta.StoredMetadata, netConfig); err != nil { + return nil, fmt.Errorf("re-register egress proxy: %w", err) + } + + // Persist updated env to metadata.json + if err := m.saveMetadata(meta); err != nil { + return nil, fmt.Errorf("save metadata: %w", err) + } + + // Return fresh instance state + return m.getInstance(ctx, id) +} diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index 161067ad..52eb11cb 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -1177,6 +1177,12 @@ type ListInstancesParams struct { Tags *Tags `json:"tags,omitempty"` } +// UpdateInstanceEnvJSONBody defines parameters for UpdateInstanceEnv. +type UpdateInstanceEnvJSONBody struct { + // Env Environment variables to update (merged with existing env) + Env map[string]string `json:"env"` +} + // GetInstanceLogsParams defines parameters for GetInstanceLogs. type GetInstanceLogsParams struct { // Tail Number of lines to return from end @@ -1264,6 +1270,9 @@ type CreateIngressJSONRequestBody = CreateIngressRequest // CreateInstanceJSONRequestBody defines body for CreateInstance for application/json ContentType. type CreateInstanceJSONRequestBody = CreateInstanceRequest +// UpdateInstanceEnvJSONRequestBody defines body for UpdateInstanceEnv for application/json ContentType. +type UpdateInstanceEnvJSONRequestBody UpdateInstanceEnvJSONBody + // ForkInstanceJSONRequestBody defines body for ForkInstance for application/json ContentType. type ForkInstanceJSONRequestBody = ForkInstanceRequest @@ -1435,6 +1444,11 @@ type ClientInterface interface { // GetInstance request GetInstance(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + // UpdateInstanceEnvWithBody request with any body + UpdateInstanceEnvWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + UpdateInstanceEnv(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // ForkInstanceWithBody request with any body ForkInstanceWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -1840,6 +1854,30 @@ func (c *Client) GetInstance(ctx context.Context, id string, reqEditors ...Reque return c.Client.Do(req) } +func (c *Client) UpdateInstanceEnvWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateInstanceEnvRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UpdateInstanceEnv(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateInstanceEnvRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) ForkInstanceWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewForkInstanceRequestWithBody(c.Server, id, contentType, body) if err != nil { @@ -3088,6 +3126,53 @@ func NewGetInstanceRequest(server string, id string) (*http.Request, error) { return req, nil } +// NewUpdateInstanceEnvRequest calls the generic UpdateInstanceEnv builder with application/json body +func NewUpdateInstanceEnvRequest(server string, id string, body UpdateInstanceEnvJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateInstanceEnvRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewUpdateInstanceEnvRequestWithBody generates requests for UpdateInstanceEnv with any type of body +func NewUpdateInstanceEnvRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/instances/%s/env", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PATCH", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewForkInstanceRequest calls the generic ForkInstance builder with application/json body func NewForkInstanceRequest(server string, id string, body ForkInstanceJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -4277,6 +4362,11 @@ type ClientWithResponsesInterface interface { // GetInstanceWithResponse request GetInstanceWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetInstanceResponse, error) + // UpdateInstanceEnvWithBodyWithResponse request with any body + UpdateInstanceEnvWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) + + UpdateInstanceEnvWithResponse(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) + // ForkInstanceWithBodyWithResponse request with any body ForkInstanceWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ForkInstanceResponse, error) @@ -4916,6 +5006,32 @@ func (r GetInstanceResponse) StatusCode() int { return 0 } +type UpdateInstanceEnvResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Instance + JSON400 *Error + JSON404 *Error + JSON409 *Error + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r UpdateInstanceEnvResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateInstanceEnvResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type ForkInstanceResponse struct { Body []byte HTTPResponse *http.Response @@ -5701,6 +5817,23 @@ func (c *ClientWithResponses) GetInstanceWithResponse(ctx context.Context, id st return ParseGetInstanceResponse(rsp) } +// UpdateInstanceEnvWithBodyWithResponse request with arbitrary body returning *UpdateInstanceEnvResponse +func (c *ClientWithResponses) UpdateInstanceEnvWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) { + rsp, err := c.UpdateInstanceEnvWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateInstanceEnvResponse(rsp) +} + +func (c *ClientWithResponses) UpdateInstanceEnvWithResponse(ctx context.Context, id string, body UpdateInstanceEnvJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateInstanceEnvResponse, error) { + rsp, err := c.UpdateInstanceEnv(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateInstanceEnvResponse(rsp) +} + // ForkInstanceWithBodyWithResponse request with arbitrary body returning *ForkInstanceResponse func (c *ClientWithResponses) ForkInstanceWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ForkInstanceResponse, error) { rsp, err := c.ForkInstanceWithBody(ctx, id, contentType, body, reqEditors...) @@ -6917,6 +7050,60 @@ func ParseGetInstanceResponse(rsp *http.Response) (*GetInstanceResponse, error) return response, nil } +// ParseUpdateInstanceEnvResponse parses an HTTP response from a UpdateInstanceEnvWithResponse call +func ParseUpdateInstanceEnvResponse(rsp *http.Response) (*UpdateInstanceEnvResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateInstanceEnvResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Instance + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseForkInstanceResponse parses an HTTP response from a ForkInstanceWithResponse call func ParseForkInstanceResponse(rsp *http.Response) (*ForkInstanceResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -7994,6 +8181,9 @@ type ServerInterface interface { // Get instance details // (GET /instances/{id}) GetInstance(w http.ResponseWriter, r *http.Request, id string) + // Update instance environment variables + // (PATCH /instances/{id}/env) + UpdateInstanceEnv(w http.ResponseWriter, r *http.Request, id string) // Fork an instance from stopped, standby, or running (with from_running=true) // (POST /instances/{id}/fork) ForkInstance(w http.ResponseWriter, r *http.Request, id string) @@ -8204,6 +8394,12 @@ func (_ Unimplemented) GetInstance(w http.ResponseWriter, r *http.Request, id st w.WriteHeader(http.StatusNotImplemented) } +// Update instance environment variables +// (PATCH /instances/{id}/env) +func (_ Unimplemented) UpdateInstanceEnv(w http.ResponseWriter, r *http.Request, id string) { + w.WriteHeader(http.StatusNotImplemented) +} + // Fork an instance from stopped, standby, or running (with from_running=true) // (POST /instances/{id}/fork) func (_ Unimplemented) ForkInstance(w http.ResponseWriter, r *http.Request, id string) { @@ -9004,6 +9200,37 @@ func (siw *ServerInterfaceWrapper) GetInstance(w http.ResponseWriter, r *http.Re handler.ServeHTTP(w, r) } +// UpdateInstanceEnv operation middleware +func (siw *ServerInterfaceWrapper) UpdateInstanceEnv(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "id" ------------- + var id string + + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.UpdateInstanceEnv(w, r, id) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // ForkInstance operation middleware func (siw *ServerInterfaceWrapper) ForkInstance(w http.ResponseWriter, r *http.Request) { @@ -9994,6 +10221,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/instances/{id}", wrapper.GetInstance) }) + r.Group(func(r chi.Router) { + r.Patch(options.BaseURL+"/instances/{id}/env", wrapper.UpdateInstanceEnv) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/instances/{id}/fork", wrapper.ForkInstance) }) @@ -10981,6 +11211,60 @@ func (response GetInstance500JSONResponse) VisitGetInstanceResponse(w http.Respo return json.NewEncoder(w).Encode(response) } +type UpdateInstanceEnvRequestObject struct { + Id string `json:"id"` + Body *UpdateInstanceEnvJSONRequestBody +} + +type UpdateInstanceEnvResponseObject interface { + VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error +} + +type UpdateInstanceEnv200JSONResponse Instance + +func (response UpdateInstanceEnv200JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv400JSONResponse Error + +func (response UpdateInstanceEnv400JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv404JSONResponse Error + +func (response UpdateInstanceEnv404JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv409JSONResponse Error + +func (response UpdateInstanceEnv409JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(409) + + return json.NewEncoder(w).Encode(response) +} + +type UpdateInstanceEnv500JSONResponse Error + +func (response UpdateInstanceEnv500JSONResponse) VisitUpdateInstanceEnvResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type ForkInstanceRequestObject struct { Id string `json:"id"` Body *ForkInstanceJSONRequestBody @@ -12030,6 +12314,9 @@ type StrictServerInterface interface { // Get instance details // (GET /instances/{id}) GetInstance(ctx context.Context, request GetInstanceRequestObject) (GetInstanceResponseObject, error) + // Update instance environment variables + // (PATCH /instances/{id}/env) + UpdateInstanceEnv(ctx context.Context, request UpdateInstanceEnvRequestObject) (UpdateInstanceEnvResponseObject, error) // Fork an instance from stopped, standby, or running (with from_running=true) // (POST /instances/{id}/fork) ForkInstance(ctx context.Context, request ForkInstanceRequestObject) (ForkInstanceResponseObject, error) @@ -12747,6 +13034,39 @@ func (sh *strictHandler) GetInstance(w http.ResponseWriter, r *http.Request, id } } +// UpdateInstanceEnv operation middleware +func (sh *strictHandler) UpdateInstanceEnv(w http.ResponseWriter, r *http.Request, id string) { + var request UpdateInstanceEnvRequestObject + + request.Id = id + + var body UpdateInstanceEnvJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.UpdateInstanceEnv(ctx, request.(UpdateInstanceEnvRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UpdateInstanceEnv") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(UpdateInstanceEnvResponseObject); ok { + if err := validResponse.VisitUpdateInstanceEnvResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // ForkInstance operation middleware func (sh *strictHandler) ForkInstance(w http.ResponseWriter, r *http.Request, id string) { var request ForkInstanceRequestObject @@ -13374,217 +13694,222 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "bsalGuz39/utz+8///8AAAD//xnIeMPtMAEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/lib/scopes/scopes.go b/lib/scopes/scopes.go index 1ebbc49a..4e692659 100644 --- a/lib/scopes/scopes.go +++ b/lib/scopes/scopes.go @@ -229,6 +229,7 @@ var RouteScopes = map[string]Scope{ "POST /instances": InstanceWrite, "DELETE /instances/{id}": InstanceDelete, "GET /instances/{id}": InstanceRead, + "PATCH /instances/{id}/env": InstanceWrite, "POST /instances/{id}/fork": InstanceWrite, "GET /instances/{id}/logs": InstanceRead, "POST /instances/{id}/restore": InstanceWrite, diff --git a/openapi.yaml b/openapi.yaml index 1f0b5e1a..f1415923 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2140,6 +2140,70 @@ paths: schema: $ref: "#/components/schemas/Error" + /instances/{id}/env: + patch: + summary: Update instance environment variables + description: | + Updates environment variables on a running instance and re-registers egress proxy + rules with the new values. This enables credential rotation without restarting the + instance. Only the provided env vars are updated; existing vars not in the request + are preserved. The instance must be running and have egress proxy enabled with + credentials configured. + operationId: updateInstanceEnv + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + description: Instance ID or name + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [env] + properties: + env: + type: object + additionalProperties: + type: string + description: Environment variables to update (merged with existing env) + responses: + 200: + description: Environment updated and egress proxy rules refreshed + content: + application/json: + schema: + $ref: "#/components/schemas/Instance" + 400: + description: Invalid request (e.g., missing required credential env vars) + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 404: + description: Instance not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 409: + description: Conflict - instance not in valid state for env update + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 500: + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /instances/{id}/stop: post: summary: Stop instance (graceful shutdown) From be53f69010fe6748b0e0869a56bec71a1ce4b9d6 Mon Sep 17 00:00:00 2001 From: sjmiller609 <7516283+sjmiller609@users.noreply.github.com> Date: Thu, 19 Mar 2026 19:37:34 +0000 Subject: [PATCH 2/2] fix: add UpdateInstanceEnv stub to builds mock The mockInstanceManager in builds tests needs to implement all Manager interface methods to compile. Co-Authored-By: Claude Opus 4.6 --- lib/builds/manager_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/builds/manager_test.go b/lib/builds/manager_test.go index a3765d5f..289af438 100644 --- a/lib/builds/manager_test.go +++ b/lib/builds/manager_test.go @@ -135,6 +135,10 @@ func (m *mockInstanceManager) StartInstance(ctx context.Context, id string, req return nil, nil } +func (m *mockInstanceManager) UpdateInstanceEnv(ctx context.Context, id string, req instances.UpdateInstanceEnvRequest) (*instances.Instance, error) { + return nil, nil +} + func (m *mockInstanceManager) StreamInstanceLogs(ctx context.Context, id string, tail int, follow bool, source instances.LogSource) (<-chan string, error) { return nil, nil }