Repository: supabase/cli
Pull request #3244 (2025-03-04 02:28:31)

Prod deploy
- **chore(deps): bump golang.org/x/oauth2 from 0.26.0 to 0.27.0 (#3203)**
- **chore(deps): bump supabase/realtime from v2.34.31 to v2.34.33 in /pkg/config/templates (#3204)**
- **chore(deps): bump supabase/supavisor from 2.3.9 to 2.3.10 in /pkg/config/templates (#3205)**
- **chore(deps): bump supabase/supavisor from 2.3.10 to 2.4.2 in /pkg/config/templates (#3209)**
- **chore(deps): bump supabase/realtime from v2.34.33 to v2.34.34 in /pkg/config/templates (#3210)**
- **fix: append config.toml when creating new function (#3212)**
- **chore(deps): bump supabase/postgres from 15.8.1.044 to 15.8.1.045 in /pkg/config/templates (#3219)**
- **fix: output standard env vars from branches get**
- **fix: filters should break to outer loop**
- **chore: update unit tests**
- **fix: accept both branch ID and name**
- **fix: support env output by default**
- **chore: dudupe output flag for gen keys**
- **fix: add pooler to start test (#3223)**
- **fix: eliminate link warning on additional url (#3222)**
- **fix: drop public views explicitly**
- **handle materialized views as well**
- **chore(deps): bump github.com/go-git/go-git/v5 from 5.13.2 to 5.14.0 (#3225)**
- **chore(deps): bump supabase/realtime from v2.34.34 to v2.34.37 in /pkg/config/templates (#3227)**
- **chore(deps): bump supabase/postgres from 15.8.1.045 to 15.8.1.046 in /pkg/config/templates (#3228)**
- **fix: remove dependency on global viper**
- **chore: parse private keys when instantiating hook**
- **fix: set openai api key as secret**
- **chore(deps): bump github.com/golangci/golangci-lint from 1.64.5 to 1.64.6 (#3235)**
- **chore(deps): bump supabase/postgres from 15.8.1.046 to 15.8.1.047 in /pkg/config/templates (#3237)**
- **chore(deps): bump supabase/realtime from v2.34.37 to v2.34.39 in /pkg/config/templates (#3236)**
- **feat(config): add rate limit configuration for emails in toml (#3206)**
- **feat: add mailpit image (#3121)**
- **chore: remove unnecessary volume mount**
- **chore(deps): bump supabase/logflare from 1.11.0 to 1.12.0 in /pkg/config/templates (#3238)**
- **chore(deps): bump github.com/docker/cli from 27.5.1+incompatible to 28.0.1+incompatible (#3218)**
- **fix: update docker client version**



Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index c1c1005b..17291720 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -18,6 +18,6 @@ updates:
       interval: "daily"
     ignore:
       - dependency-name: "library/kong"
-      - dependency-name: "inbucket/inbucket"
+      - dependency-name: "axllent/mailpit"
       - dependency-name: "darthsim/imgproxy"
       - dependency-name: "timberio/vector"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ce32d176..d6bdee17 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -56,6 +56,7 @@ jobs:
           cache: true
       - run: go build main.go
       - run: ./main init
+      - run: sed -i '/\[db.pooler\]/{n;s/.*/enabled = true/}' supabase/config.toml
       - run: ./main start
         env:
           SUPABASE_INTERNAL_IMAGE_REGISTRY: ghcr.io
diff --git a/cmd/branches.go b/cmd/branches.go
index b541704b..1c093278 100644
--- a/cmd/branches.go
+++ b/cmd/branches.go
@@ -6,6 +6,7 @@ import (
 	"os"
 
 	"github.com/go-errors/errors"
+	"github.com/google/uuid"
 	"github.com/spf13/afero"
 	"github.com/spf13/cobra"
 	"github.com/supabase/cli/internal/branches/create"
@@ -75,14 +76,11 @@ var (
 		Args:  cobra.MaximumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx := cmd.Context()
-			if len(args) == 0 {
-				if err := promptBranchId(ctx, flags.ProjectRef); err != nil {
-					return err
-				}
-			} else {
-				branchId = args[0]
+			fsys := afero.NewOsFs()
+			if err := promptBranchId(ctx, args, fsys); err != nil {
+				return err
 			}
-			return get.Run(ctx, branchId, afero.NewOsFs())
+			return get.Run(ctx, branchId, fsys)
 		},
 	}
 
@@ -101,7 +99,7 @@ var (
 	branchUpdateCmd = &cobra.Command{
 		Use:   "update [branch-id]",
 		Short: "Update a preview branch",
-		Long:  "Update a preview branch by its ID.",
+		Long:  "Update a preview branch by its name or ID.",
 		Args:  cobra.MaximumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmdFlags := cmd.Flags()
@@ -119,30 +117,24 @@ var (
 				body.Status = (*api.UpdateBranchBodyStatus)(&branchStatus.Value)
 			}
 			ctx := cmd.Context()
-			if len(args) == 0 {
-				if err := promptBranchId(ctx, flags.ProjectRef); err != nil {
-					return err
-				}
-			} else {
-				branchId = args[0]
+			fsys := afero.NewOsFs()
+			if err := promptBranchId(ctx, args, fsys); err != nil {
+				return err
 			}
-			return update.Run(cmd.Context(), branchId, body, afero.NewOsFs())
+			return update.Run(cmd.Context(), branchId, body, fsys)
 		},
 	}
 
 	branchDeleteCmd = &cobra.Command{
 		Use:   "delete [branch-id]",
 		Short: "Delete a preview branch",
-		Long:  "Delete a preview branch by its ID.",
+		Long:  "Delete a preview branch by its name or ID.",
 		Args:  cobra.MaximumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx := cmd.Context()
-			if len(args) == 0 {
-				if err := promptBranchId(ctx, flags.ProjectRef); err != nil {
-					return err
-				}
-			} else {
-				branchId = args[0]
+			fsys := afero.NewOsFs()
+			if err := promptBranchId(ctx, args, fsys); err != nil {
+				return err
 			}
 			return delete.Run(ctx, branchId)
 		},
@@ -165,8 +157,6 @@ func init() {
 	createFlags.Var(&branchRegion, "region", "Select a region to deploy the branch database.")
 	createFlags.Var(&size, "size", "Select a desired instance size for the branch database.")
 	createFlags.BoolVar(&persistent, "persistent", false, "Whether to create a persistent branch.")
-	getFlags := branchGetCmd.Flags()
-	getFlags.VarP(&utils.OutputFormat, "output", "o", "Output format of branch details.")
 	branchesCmd.AddCommand(branchCreateCmd)
 	branchesCmd.AddCommand(branchListCmd)
 	branchesCmd.AddCommand(branchGetCmd)
@@ -181,40 +171,43 @@ func init() {
 	rootCmd.AddCommand(branchesCmd)
 }
 
-func promptBranchId(ctx context.Context, ref string) error {
-	resp, err := utils.GetSupabase().V1ListAllBranchesWithResponse(ctx, ref)
-	if err != nil {
-		return errors.Errorf("failed to list preview branches: %w", err)
-	}
-	if resp.JSON200 == nil {
-		return errors.New("Unexpected error listing preview branches: " + string(resp.Body))
-	}
-	console := utils.NewConsole()
-	if !console.IsTTY {
-		// Fallback to current git branch on GHA
-		gitBranch := keys.GetGitBranch(afero.NewOsFs())
-		title := "Enter the name of your branch: "
-		if len(gitBranch) > 0 {
-			title = fmt.Sprintf("%-2s (or leave blank to use %s): ", title, utils.Aqua(gitBranch))
+func promptBranchId(ctx context.Context, args []string, fsys afero.Fs) error {
+	var filter []list.BranchFilter
+	if len(args) > 0 {
+		if branchId = args[0]; uuid.Validate(branchId) == nil {
+			return nil
+		}
+		// Try resolving as branch name
+		filter = append(filter, list.FilterByName(branchId))
+	} else if console := utils.NewConsole(); !console.IsTTY {
+		// Only read from stdin if the terminal is non-interactive
+		title := "Enter the name of your branch"
+		if branchId = keys.GetGitBranch(fsys); len(branchId) > 0 {
+			title += fmt.Sprintf(" (or leave blank to use %s)", utils.Aqua(branchId))
 		}
+		title += ": "
 		if name, err := console.PromptText(ctx, title); err != nil {
 			return err
 		} else if len(name) > 0 {
-			gitBranch = name
+			branchId = name
 		}
-		if len(gitBranch) == 0 {
-			return errors.New("git branch cannot be empty")
+		if len(branchId) == 0 {
+			return errors.New("branch name cannot be empty")
 		}
-		for _, branch := range *resp.JSON200 {
-			if branch.Name == gitBranch {
-				branchId = branch.Id
-				return nil
-			}
-		}
-		return errors.Errorf("Branch not found: %s", gitBranch)
+		filter = append(filter, list.FilterByName(branchId))
+	}
+	branches, err := list.ListBranch(ctx, flags.ProjectRef, filter...)
+	if err != nil {
+		return err
+	} else if len(branches) == 0 {
+		return errors.Errorf("branch not found: %s", branchId)
+	} else if len(branches) == 1 {
+		branchId = branches[0].Id
+		return nil
 	}
-	items := make([]utils.PromptItem, len(*resp.JSON200))
-	for i, branch := range *resp.JSON200 {
+	// Let user choose from a list of branches
+	items := make([]utils.PromptItem, len(branches))
+	for i, branch := range branches {
 		items[i] = utils.PromptItem{
 			Summary: branch.Name,
 			Details: branch.Id,
diff --git a/cmd/gen.go b/cmd/gen.go
index 7f936bda..5849a5ce 100644
--- a/cmd/gen.go
+++ b/cmd/gen.go
@@ -21,16 +21,7 @@ var (
 		Short:   "Run code generation tools",
 	}
 
-	keyNames  keys.CustomName
-	keyOutput = utils.EnumFlag{
-		Allowed: []string{
-			utils.OutputEnv,
-			utils.OutputJson,
-			utils.OutputToml,
-			utils.OutputYaml,
-		},
-		Value: utils.OutputEnv,
-	}
+	keyNames keys.CustomName
 
 	genKeysCmd = &cobra.Command{
 		Use:   "keys",
@@ -47,7 +38,11 @@ var (
 			return cmd.Root().PersistentPreRunE(cmd, args)
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return keys.Run(cmd.Context(), flags.ProjectRef, keyOutput.Value, keyNames, afero.NewOsFs())
+			format := utils.OutputFormat.Value
+			if format == utils.OutputPretty {
+				format = utils.OutputEnv
+			}
+			return keys.Run(cmd.Context(), flags.ProjectRef, format, keyNames, afero.NewOsFs())
 		},
 	}
 
@@ -114,7 +109,6 @@ func init() {
 	genCmd.AddCommand(genTypesCmd)
 	keyFlags := genKeysCmd.Flags()
 	keyFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.")
-	keyFlags.VarP(&keyOutput, "output", "o", "Output format of key variables.")
 	keyFlags.StringSliceVar(&override, "override-name", []string{}, "Override specific variable names.")
 	genCmd.AddCommand(genKeysCmd)
 	rootCmd.AddCommand(genCmd)
diff --git a/cmd/status.go b/cmd/status.go
index 13540bfb..11b77485 100644
--- a/cmd/status.go
+++ b/cmd/status.go
@@ -14,10 +14,6 @@ import (
 var (
 	override []string
 	names    status.CustomName
-	output   = utils.EnumFlag{
-		Allowed: append([]string{utils.OutputEnv}, utils.OutputDefaultAllowed...),
-		Value:   utils.OutputPretty,
-	}
 
 	statusCmd = &cobra.Command{
 		GroupID: groupLocalDev,
@@ -32,7 +28,7 @@ var (
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return status.Run(ctx, names, output.Value, afero.NewOsFs())
+			return status.Run(ctx, names, utils.OutputFormat.Value, afero.NewOsFs())
 		},
 		Example: `  supabase status -o env --override-name api.url=NEXT_PUBLIC_SUPABASE_URL
   supabase status -o json`,
@@ -41,7 +37,6 @@ var (
 
 func init() {
 	flags := statusCmd.Flags()
-	flags.VarP(&output, "output", "o", "Output format of status variables.")
 	flags.StringSliceVar(&override, "override-name", []string{}, "Override specific variable names.")
 	rootCmd.AddCommand(statusCmd)
 }
diff --git a/examples/functions-deploy/main.go b/examples/functions-deploy/main.go
index 716ef146..926e8b93 100644
--- a/examples/functions-deploy/main.go
+++ b/examples/functions-deploy/main.go
@@ -19,17 +19,15 @@ func main() {
 	}
 }
 
-// Requires edge runtime binary to be added to PATH
 func deploy(ctx context.Context, fsys fs.FS) error {
 	project := os.Getenv("SUPABASE_PROJECT_ID")
 	apiClient := newAPIClient(os.Getenv("SUPABASE_ACCESS_TOKEN"))
-	eszipBundler := function.NewNativeBundler(".", fsys)
-	functionClient := function.NewEdgeRuntimeAPI(project, apiClient, eszipBundler)
+	functionClient := function.NewEdgeRuntimeAPI(project, apiClient)
 	fc := config.FunctionConfig{"my-slug": {
 		Entrypoint: "supabase/functions/my-slug/index.ts",
 		ImportMap:  "supabase/functions/import_map.json",
 	}}
-	return functionClient.UpsertFunctions(ctx, fc)
+	return functionClient.Deploy(ctx, fc, fsys)
 }
 
 func newAPIClient(token string) api.ClientWithResponses {
diff --git a/go.mod b/go.mod
index 7d18dd2c..fdf5a7d3 100644
--- a/go.mod
+++ b/go.mod
@@ -11,18 +11,18 @@ require (
 	github.com/charmbracelet/bubbletea v0.25.0
 	github.com/charmbracelet/glamour v0.7.0
 	github.com/charmbracelet/lipgloss v0.12.1
-	github.com/containers/common v0.62.0
-	github.com/docker/cli v27.5.1+incompatible
-	github.com/docker/docker v27.5.1+incompatible
+	github.com/containers/common v0.62.1
+	github.com/docker/cli v28.0.1+incompatible
+	github.com/docker/docker v28.0.1+incompatible
 	github.com/docker/go-connections v0.5.0
 	github.com/docker/go-units v0.5.0
 	github.com/ecies/go/v2 v2.0.10
 	github.com/getsentry/sentry-go v0.31.1
 	github.com/go-errors/errors v1.5.1
-	github.com/go-git/go-git/v5 v5.13.2
+	github.com/go-git/go-git/v5 v5.14.0
 	github.com/go-xmlfmt/xmlfmt v1.1.3
 	github.com/golang-jwt/jwt/v5 v5.2.1
-	github.com/golangci/golangci-lint v1.64.5
+	github.com/golangci/golangci-lint v1.64.6
 	github.com/google/go-github/v62 v62.0.0
 	github.com/google/go-querystring v1.1.0
 	github.com/google/uuid v1.6.0
@@ -48,21 +48,21 @@ require (
 	github.com/tidwall/jsonc v0.3.2
 	github.com/withfig/autocomplete-tools/packages/cobra v1.2.0
 	github.com/zalando/go-keyring v0.2.6
-	go.opentelemetry.io/otel v1.34.0
-	golang.org/x/mod v0.23.0
-	golang.org/x/oauth2 v0.26.0
-	golang.org/x/term v0.29.0
-	google.golang.org/grpc v1.70.0
+	go.opentelemetry.io/otel v1.35.0
+	golang.org/x/mod v0.24.0
+	golang.org/x/oauth2 v0.28.0
+	golang.org/x/term v0.30.0
+	google.golang.org/grpc v1.71.0
 	gopkg.in/yaml.v3 v3.0.1
 	gotest.tools/gotestsum v1.12.0
 )
 
 require (
-	4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
+	4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
 	4d63.com/gochecknoglobals v0.2.2 // indirect
 	al.essio.dev/pkg/shellescape v1.5.1 // indirect
 	dario.cat/mergo v1.0.1 // indirect
-	github.com/4meepo/tagalign v1.4.1 // indirect
+	github.com/4meepo/tagalign v1.4.2 // indirect
 	github.com/Abirdcfly/dupword v0.1.3 // indirect
 	github.com/Antonboom/errname v1.0.0 // indirect
 	github.com/Antonboom/nilnil v1.0.1 // indirect
@@ -70,7 +70,7 @@ require (
 	github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
 	github.com/Crocmagnon/fatcontext v0.7.1 // indirect
 	github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
-	github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect
+	github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect
 	github.com/Masterminds/semver/v3 v3.3.0 // indirect
 	github.com/Microsoft/go-winio v0.6.2 // indirect
 	github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
@@ -96,7 +96,7 @@ require (
 	github.com/breml/errchkjson v0.4.0 // indirect
 	github.com/butuzov/ireturn v0.3.1 // indirect
 	github.com/butuzov/mirror v1.3.0 // indirect
-	github.com/catenacyber/perfsprint v0.8.1 // indirect
+	github.com/catenacyber/perfsprint v0.8.2 // indirect
 	github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/charithe/durationcheck v0.0.10 // indirect
@@ -104,12 +104,12 @@ require (
 	github.com/charmbracelet/x/ansi v0.1.4 // indirect
 	github.com/chavacava/garif v0.1.0 // indirect
 	github.com/ckaznocha/intrange v0.3.0 // indirect
-	github.com/cloudflare/circl v1.3.7 // indirect
+	github.com/cloudflare/circl v1.6.0 // indirect
 	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
 	github.com/containerd/log v0.1.0 // indirect
 	github.com/containers/storage v1.57.1 // indirect
 	github.com/curioswitch/go-reassign v0.3.0 // indirect
-	github.com/cyphar/filepath-securejoin v0.3.6 // indirect
+	github.com/cyphar/filepath-securejoin v0.4.1 // indirect
 	github.com/daixiang0/gci v0.13.5 // indirect
 	github.com/danieljoos/wincred v1.2.2 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -154,7 +154,7 @@ require (
 	github.com/godbus/dbus/v5 v5.1.0 // indirect
 	github.com/gofrs/flock v0.12.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
 	github.com/golangci/go-printf-func-name v0.1.0 // indirect
@@ -163,14 +163,14 @@ require (
 	github.com/golangci/plugin-module-register v0.1.1 // indirect
 	github.com/golangci/revgrep v0.8.0 // indirect
 	github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
-	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/go-cmp v0.7.0 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/gordonklaus/ineffassign v0.1.0 // indirect
 	github.com/gorilla/css v1.0.0 // indirect
 	github.com/gorilla/mux v1.8.1 // indirect
 	github.com/gorilla/websocket v1.5.0 // indirect
 	github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
-	github.com/gostaticanalysis/comment v1.4.2 // indirect
+	github.com/gostaticanalysis/comment v1.5.0 // indirect
 	github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
 	github.com/gostaticanalysis/nilerr v0.1.1 // indirect
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
@@ -194,14 +194,14 @@ require (
 	github.com/julz/importas v0.2.0 // indirect
 	github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
-	github.com/kisielk/errcheck v1.8.0 // indirect
-	github.com/kkHAIKE/contextcheck v1.1.5 // indirect
+	github.com/kisielk/errcheck v1.9.0 // indirect
+	github.com/kkHAIKE/contextcheck v1.1.6 // indirect
 	github.com/kr/pretty v0.3.1 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/kulti/thelper v0.6.3 // indirect
 	github.com/kunwardeep/paralleltest v1.0.10 // indirect
 	github.com/lasiar/canonicalheader v1.1.2 // indirect
-	github.com/ldez/exptostd v0.4.1 // indirect
+	github.com/ldez/exptostd v0.4.2 // indirect
 	github.com/ldez/gomoddirectives v0.6.1 // indirect
 	github.com/ldez/grignotin v0.9.0 // indirect
 	github.com/ldez/tagliatelle v0.7.1 // indirect
@@ -220,7 +220,7 @@ require (
 	github.com/mattn/go-localereader v0.0.1 // indirect
 	github.com/mattn/go-runewidth v0.0.16 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
-	github.com/mgechev/revive v1.6.1 // indirect
+	github.com/mgechev/revive v1.7.0 // indirect
 	github.com/microcosm-cc/bluemonday v1.0.25 // indirect
 	github.com/miekg/pkcs11 v1.1.1 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
@@ -237,7 +237,7 @@ require (
 	github.com/nakabonne/nestif v0.3.1 // indirect
 	github.com/nishanths/exhaustive v0.12.0 // indirect
 	github.com/nishanths/predeclared v0.2.2 // indirect
-	github.com/nunnatsa/ginkgolinter v0.19.0 // indirect
+	github.com/nunnatsa/ginkgolinter v0.19.1 // indirect
 	github.com/olekukonko/tablewriter v0.0.5 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.1.0 // indirect
@@ -258,7 +258,7 @@ require (
 	github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
 	github.com/raeperd/recvcheck v0.2.0 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
-	github.com/rogpeppe/go-internal v1.13.1 // indirect
+	github.com/rogpeppe/go-internal v1.14.1 // indirect
 	github.com/ryancurrah/gomodguard v1.3.5 // indirect
 	github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
 	github.com/sagikazarmark/locafero v0.4.0 // indirect
@@ -273,7 +273,7 @@ require (
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/sivchari/containedctx v1.0.3 // indirect
 	github.com/sivchari/tenv v1.12.1 // indirect
-	github.com/skeema/knownhosts v1.3.0 // indirect
+	github.com/skeema/knownhosts v1.3.1 // indirect
 	github.com/sonatard/noctx v0.1.0 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/sourcegraph/go-diff v0.7.0 // indirect
@@ -283,8 +283,8 @@ require (
 	github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect
 	github.com/stretchr/objx v0.5.2 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
-	github.com/tdakkota/asciicheck v0.4.0 // indirect
-	github.com/tetafro/godot v1.4.20 // indirect
+	github.com/tdakkota/asciicheck v0.4.1 // indirect
+	github.com/tetafro/godot v1.5.0 // indirect
 	github.com/theupdateframework/notary v0.7.0 // indirect
 	github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 // indirect
 	github.com/timonwong/loggercheck v0.10.1 // indirect
@@ -314,21 +314,21 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
-	go.opentelemetry.io/otel/metric v1.34.0 // indirect
+	go.opentelemetry.io/otel/metric v1.35.0 // indirect
 	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
 	go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
-	go.opentelemetry.io/otel/trace v1.34.0 // indirect
+	go.opentelemetry.io/otel/trace v1.35.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/automaxprocs v1.6.0 // indirect
 	go.uber.org/multierr v1.9.0 // indirect
 	go.uber.org/zap v1.24.0 // indirect
-	golang.org/x/crypto v0.33.0 // indirect
+	golang.org/x/crypto v0.35.0 // indirect
 	golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
 	golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect
 	golang.org/x/net v0.35.0 // indirect
 	golang.org/x/sync v0.11.0 // indirect
-	golang.org/x/sys v0.30.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
 	golang.org/x/text v0.22.0 // indirect
 	golang.org/x/tools v0.30.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
diff --git a/go.sum b/go.sum
index e6bcbdda..f9941eb8 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA=
-4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
+4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A=
+4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY=
 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU=
 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0=
 al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
@@ -39,8 +39,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
 dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
 dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/4meepo/tagalign v1.4.1 h1:GYTu2FaPGOGb/xJalcqHeD4il5BiCywyEYZOA55P6J4=
-github.com/4meepo/tagalign v1.4.1/go.mod h1:2H9Yu6sZ67hmuraFgfZkNcg5Py9Ch/Om9l2K/2W1qS4=
+github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E=
+github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI=
 github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
 github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
 github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA=
@@ -59,8 +59,8 @@ github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVU
 github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU=
 github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
 github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
-github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU=
-github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao=
+github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k=
+github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg=
 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
 github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
 github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
@@ -148,8 +148,8 @@ github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY
 github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M=
 github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
 github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
-github.com/catenacyber/perfsprint v0.8.1 h1:bGOHuzHe0IkoGeY831RW4aSlt1lPRd3WRAScSWOaV7E=
-github.com/catenacyber/perfsprint v0.8.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50=
+github.com/catenacyber/perfsprint v0.8.2 h1:+o9zVmCSVa7M4MvabsWvESEhpsMkhfE7k0sHNGL95yw=
+github.com/catenacyber/perfsprint v0.8.2/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM=
 github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
 github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
@@ -183,8 +183,8 @@ github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9Duwj
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
 github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
-github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
-github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
+github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
+github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
@@ -192,8 +192,8 @@ github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2
 github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
 github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
 github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
-github.com/containers/common v0.62.0 h1:Sl9WE5h7Y/F3bejrMAA4teP1EcY9ygqJmW4iwSloZ10=
-github.com/containers/common v0.62.0/go.mod h1:Yec+z8mrSq4rydHofrnDCBqAcNA/BGrSg1kfFUL6F6s=
+github.com/containers/common v0.62.1 h1:durvu7Kelb8PYgX7bwuAg/d5LKj2hs3cAaqcU7Vnqus=
+github.com/containers/common v0.62.1/go.mod h1:n9cEboBmY3AnTk1alkq4t7sLM4plwkDCiaWbsf67YxE=
 github.com/containers/storage v1.57.1 h1:hKPoFsuBcB3qTzBxa4IFpZMRzUuL5Xhv/BE44W0XHx8=
 github.com/containers/storage v1.57.1/go.mod h1:i/Hb4lu7YgFr9G0K6BMjqW0BLJO1sFsnWQwj2UoWCUM=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@@ -205,8 +205,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
 github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
-github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
-github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
+github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
+github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
 github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c=
 github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
 github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
@@ -226,13 +226,13 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK
 github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk=
 github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE=
-github.com/docker/cli v27.5.1+incompatible h1:JB9cieUT9YNiMITtIsguaN55PLOHhBSz3LKVc6cqWaY=
-github.com/docker/cli v27.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
+github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8=
-github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
+github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
 github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
 github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@@ -253,8 +253,8 @@ github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5ql
 github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
 github.com/ecies/go/v2 v2.0.10 h1:AaLxGio0MLLbvWur4rKnLzw+K9zI+wMScIDAtqCqOtU=
 github.com/ecies/go/v2 v2.0.10/go.mod h1:N73OyuR6tuKznit2LhXjrZ0XAQ234uKbzYz8pEPYzlI=
-github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
-github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
+github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
+github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -305,8 +305,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
 github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
-github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
+github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
+github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -380,8 +380,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -413,8 +413,8 @@ github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUP
 github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s=
 github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE=
 github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY=
-github.com/golangci/golangci-lint v1.64.5 h1:5omC86XFBKXZgCrVdUWU+WNHKd+CWCxNx717KXnzKZY=
-github.com/golangci/golangci-lint v1.64.5/go.mod h1:WZnwq8TF0z61h3jLQ7Sk5trcP7b3kUFxLD6l1ivtdvU=
+github.com/golangci/golangci-lint v1.64.6 h1:jOLaQN41IV7bMzXuNC4UnQGll7N1xY6eFDXkXEPGKAs=
+github.com/golangci/golangci-lint v1.64.6/go.mod h1:Wz9q+6EVuqGQ94GQ96RB2mjpcZYTOGhBhbt4O7REPu4=
 github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
 github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
 github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
@@ -441,8 +441,9 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4=
 github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4=
 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@@ -480,8 +481,9 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
 github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
 github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
 github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
-github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q=
 github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
+github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8=
+github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc=
 github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk=
 github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY=
 github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk=
@@ -608,11 +610,11 @@ github.com/karamaru-alpha/copyloopvar v1.2.1/go.mod h1:nFmMlFNlClC2BPvNaHMdkirmT
 github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg=
-github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
+github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
+github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg=
-github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA=
+github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE=
+github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -633,8 +635,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4=
 github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
-github.com/ldez/exptostd v0.4.1 h1:DIollgQ3LWZMp3HJbSXsdE2giJxMfjyHj3eX4oiD6JU=
-github.com/ldez/exptostd v0.4.1/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
+github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs=
+github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
 github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc=
 github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs=
 github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow=
@@ -692,8 +694,8 @@ github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mgechev/revive v1.6.1 h1:ncK0ZCMWtb8GXwVAmk+IeWF2ULIDsvRxSRfg5sTwQ2w=
-github.com/mgechev/revive v1.6.1/go.mod h1:/2tfHWVO8UQi/hqJsIYNEKELi+DJy/e+PQpLgTB1v88=
+github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY=
+github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4=
 github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
 github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
 github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@@ -742,8 +744,8 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
 github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
 github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
 github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
-github.com/nunnatsa/ginkgolinter v0.19.0 h1:CnHRFAeBS3LdLI9h+Jidbcc5KH71GKOmaBZQk8Srnto=
-github.com/nunnatsa/ginkgolinter v0.19.0/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
+github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4=
+github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -854,8 +856,8 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
-github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
-github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
@@ -901,8 +903,8 @@ github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+W
 github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
 github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY=
 github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw=
-github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
-github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
+github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
+github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
 github.com/slack-go/slack v0.16.0 h1:khp/WCFv+Hb/B/AJaAwvcxKun0hM6grN0bUZ8xG60P8=
 github.com/slack-go/slack v0.16.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
 github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM=
@@ -956,14 +958,14 @@ github.com/stripe/pg-schema-diff v0.8.0 h1:Ggm4yDbPtaflYQLV3auEMTLxQPaentV/wmDEo
 github.com/stripe/pg-schema-diff v0.8.0/go.mod h1:HuTBuWLuvnY9g9nptbSD58xugN19zSJNkF4w/sYRtdU=
 github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
-github.com/tdakkota/asciicheck v0.4.0 h1:VZ13Itw4k1i7d+dpDSNS8Op645XgGHpkCEh/WHicgWw=
-github.com/tdakkota/asciicheck v0.4.0/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
+github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8=
+github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
 github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
 github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
 github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
 github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
-github.com/tetafro/godot v1.4.20 h1:z/p8Ek55UdNvzt4TFn2zx2KscpW4rWqcnUrdmvWJj7E=
-github.com/tetafro/godot v1.4.20/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
+github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw=
+github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
 github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
 github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
 github.com/tidwall/jsonc v0.3.2 h1:ZTKrmejRlAJYdn0kcaFqRAKlxxFIC21pYq8vLa4p2Wc=
@@ -1040,8 +1042,8 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
-go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
-go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 h1:ajl4QczuJVA2TU9W9AGw++86Xga/RKt//16z/yxPgdk=
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0/go.mod h1:Vn3/rlOJ3ntf/Q3zAI0V5lDnTbHGaUsNUeF6nZmm7pA=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
@@ -1050,14 +1052,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0u
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
-go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
-go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
 go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
 go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
 go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
 go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
-go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
-go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
 go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
 go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -1104,8 +1106,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
 golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
-golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
+golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1154,8 +1156,8 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
-golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
+golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1214,8 +1216,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
-golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
+golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1305,8 +1307,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1321,8 +1323,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
 golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
 golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
-golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
-golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1488,8 +1490,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
-google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
+google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/internal/branches/get/get.go b/internal/branches/get/get.go
index 387e4ab7..eebd8b4c 100644
--- a/internal/branches/get/get.go
+++ b/internal/branches/get/get.go
@@ -9,19 +9,55 @@ import (
 	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/migration/list"
+	"github.com/supabase/cli/internal/projects/apiKeys"
 	"github.com/supabase/cli/internal/utils"
+	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/cast"
 )
 
 func Run(ctx context.Context, branchId string, fsys afero.Fs) error {
-	resp, err := utils.GetSupabase().V1GetABranchConfigWithResponse(ctx, branchId)
+	detail, err := getBranchDetail(ctx, branchId)
 	if err != nil {
-		return errors.Errorf("failed to retrieve preview branch: %w", err)
+		return err
 	}
-	if resp.JSON200 == nil {
-		return errors.New("Unexpected error retrieving preview branch: " + string(resp.Body))
+
+	if utils.OutputFormat.Value != utils.OutputPretty {
+		keys, err := apiKeys.RunGetApiKeys(ctx, detail.Ref)
+		if err != nil {
+			return err
+		}
+		pooler, err := getPoolerConfig(ctx, detail.Ref)
+		if err != nil {
+			return err
+		}
+		envs := toStandardEnvs(detail, pooler, keys)
+		return utils.EncodeOutput(utils.OutputFormat.Value, os.Stdout, envs)
 	}
 
+	table := `|HOST|PORT|USER|PASSWORD|JWT SECRET|POSTGRES VERSION|STATUS|
+|-|-|-|-|-|-|-|
+` + fmt.Sprintf(
+		"|`%s`|`%d`|`%s`|`%s`|`%s`|`%s`|`%s`|\n",
+		detail.DbHost,
+		detail.DbPort,
+		*detail.DbUser,
+		*detail.DbPass,
+		*detail.JwtSecret,
+		detail.PostgresVersion,
+		detail.Status,
+	)
+
+	return list.RenderTable(table)
+}
+
+func getBranchDetail(ctx context.Context, branchId string) (api.BranchDetailResponse, error) {
+	var result api.BranchDetailResponse
+	resp, err := utils.GetSupabase().V1GetABranchConfigWithResponse(ctx, branchId)
+	if err != nil {
+		return result, errors.Errorf("failed to get branch: %w", err)
+	} else if resp.JSON200 == nil {
+		return result, errors.Errorf("unexpected get branch status %d: %s", resp.StatusCode(), string(resp.Body))
+	}
 	masked := "******"
 	if resp.JSON200.DbUser == nil {
 		resp.JSON200.DbUser = &masked
@@ -32,35 +68,42 @@ func Run(ctx context.Context, branchId string, fsys afero.Fs) error {
 	if resp.JSON200.JwtSecret == nil {
 		resp.JSON200.JwtSecret = &masked
 	}
+	return *resp.JSON200, nil
+}
 
-	config := pgconn.Config{
-		Host:     resp.JSON200.DbHost,
-		Port:     cast.UIntToUInt16(cast.IntToUint(resp.JSON200.DbPort)),
-		User:     *resp.JSON200.DbUser,
-		Password: *resp.JSON200.DbPass,
+func getPoolerConfig(ctx context.Context, ref string) (api.SupavisorConfigResponse, error) {
+	var result api.SupavisorConfigResponse
+	resp, err := utils.GetSupabase().V1GetSupavisorConfigWithResponse(ctx, ref)
+	if err != nil {
+		return result, errors.Errorf("failed to get pooler: %w", err)
+	} else if resp.JSON200 == nil {
+		return result, errors.Errorf("unexpected get pooler status %d: %s", resp.StatusCode(), string(resp.Body))
 	}
-
-	postgresConnectionString := utils.ToPostgresURL(config)
-	if utils.OutputFormat.Value != utils.OutputPretty {
-		envs := map[string]string{
-			"POSTGRES_URL": postgresConnectionString,
+	for _, config := range *resp.JSON200 {
+		if config.DatabaseType == api.PRIMARY {
+			return config, nil
 		}
-		return utils.EncodeOutput(utils.OutputFormat.Value, os.Stdout, envs)
 	}
+	return result, errors.Errorf("primary database not found: %s", ref)
+}
 
-	table := `|HOST|PORT|USER|PASSWORD|JWT SECRET|POSTGRES VERSION|STATUS|POSTGRES URL|
-|-|-|-|-|-|-|-|-|
-` + fmt.Sprintf(
-		"|`%s`|`%d`|`%s`|`%s`|`%s`|`%s`|`%s`|`%s`|\n",
-		resp.JSON200.DbHost,
-		resp.JSON200.DbPort,
-		*resp.JSON200.DbUser,
-		*resp.JSON200.DbPass,
-		*resp.JSON200.JwtSecret,
-		resp.JSON200.PostgresVersion,
-		resp.JSON200.Status,
-		postgresConnectionString,
-	)
-
-	return list.RenderTable(table)
+func toStandardEnvs(detail api.BranchDetailResponse, pooler api.SupavisorConfigResponse, keys []api.ApiKeyResponse) map[string]string {
+	direct := pgconn.Config{
+		Host:     detail.DbHost,
+		Port:     cast.UIntToUInt16(cast.IntToUint(detail.DbPort)),
+		User:     *detail.DbUser,
+		Password: *detail.DbPass,
+	}
+	config, err := utils.ParsePoolerURL(pooler.ConnectionString)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), err)
+		config = &direct
+	} else {
+		config.Password = direct.Password
+	}
+	envs := apiKeys.ToEnv(keys)
+	envs["POSTGRES_URL"] = utils.ToPostgresURL(*config)
+	envs["POSTGRES_URL_NON_POOLING"] = utils.ToPostgresURL(direct)
+	envs["SUPABASE_URL"] = "https://" + utils.GetSupabaseHost(detail.Ref)
+	return envs
 }
diff --git a/internal/branches/list/list.go b/internal/branches/list/list.go
index 79eb75e2..fd40da97 100644
--- a/internal/branches/list/list.go
+++ b/internal/branches/list/list.go
@@ -10,22 +10,19 @@ import (
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/flags"
+	"github.com/supabase/cli/pkg/api"
 )
 
 func Run(ctx context.Context, fsys afero.Fs) error {
-	resp, err := utils.GetSupabase().V1ListAllBranchesWithResponse(ctx, flags.ProjectRef)
+	branches, err := ListBranch(ctx, flags.ProjectRef)
 	if err != nil {
-		return errors.Errorf("failed to list preview branches: %w", err)
-	}
-
-	if resp.JSON200 == nil {
-		return errors.New("Unexpected error listing preview branches: " + string(resp.Body))
+		return err
 	}
 
 	table := `|ID|NAME|DEFAULT|GIT BRANCH|STATUS|CREATED AT (UTC)|UPDATED AT (UTC)|
 |-|-|-|-|-|-|-|
 `
-	for _, branch := range *resp.JSON200 {
+	for _, branch := range branches {
 		gitBranch := " "
 		if branch.GitBranch != nil {
 			gitBranch = *branch.GitBranch
@@ -44,3 +41,31 @@ func Run(ctx context.Context, fsys afero.Fs) error {
 
 	return list.RenderTable(table)
 }
+
+type BranchFilter func(api.BranchResponse) bool
+
+func ListBranch(ctx context.Context, ref string, filter ...BranchFilter) ([]api.BranchResponse, error) {
+	resp, err := utils.GetSupabase().V1ListAllBranchesWithResponse(ctx, ref)
+	if err != nil {
+		return nil, errors.Errorf("failed to list branch: %w", err)
+	} else if resp.JSON200 == nil {
+		return nil, errors.Errorf("unexpected list branch status %d: %s", resp.StatusCode(), string(resp.Body))
+	}
+	var result []api.BranchResponse
+OUTER:
+	for _, branch := range *resp.JSON200 {
+		for _, keep := range filter {
+			if !keep(branch) {
+				continue OUTER
+			}
+		}
+		result = append(result, branch)
+	}
+	return result, nil
+}
+
+func FilterByName(branchName string) BranchFilter {
+	return func(br api.BranchResponse) bool {
+		return br.Name == branchName
+	}
+}
diff --git a/internal/functions/deploy/deploy.go b/internal/functions/deploy/deploy.go
index 33ba5c23..8686a426 100644
--- a/internal/functions/deploy/deploy.go
+++ b/internal/functions/deploy/deploy.go
@@ -36,12 +36,12 @@ func Run(ctx context.Context, slugs []string, useDocker bool, noVerifyJWT *bool,
 	if err != nil {
 		return err
 	}
+	opt := function.WithMaxJobs(maxJobs)
 	if useDocker {
-		api := function.NewEdgeRuntimeAPI(flags.ProjectRef, *utils.GetSupabase(), NewDockerBundler(fsys))
-		if err := api.UpsertFunctions(ctx, functionConfig); err != nil {
-			return err
-		}
-	} else if err := deploy(ctx, functionConfig, maxJobs, fsys); errors.Is(err, errNoDeploy) {
+		opt = function.WithBundler(NewDockerBundler(fsys))
+	}
+	api := function.NewEdgeRuntimeAPI(flags.ProjectRef, *utils.GetSupabase(), opt)
+	if err := api.Deploy(ctx, functionConfig, afero.NewIOFS(fsys)); errors.Is(err, function.ErrNoDeploy) {
 		fmt.Fprintln(os.Stderr, err)
 		return nil
 	} else if err != nil {
diff --git a/internal/functions/new/new.go b/internal/functions/new/new.go
index 67a9893e..877928c8 100644
--- a/internal/functions/new/new.go
+++ b/internal/functions/new/new.go
@@ -21,8 +21,11 @@ var (
 	denoEmbed string
 	//go:embed templates/.npmrc
 	npmrcEmbed string
+	//go:embed templates/config.toml
+	configEmbed string
 
-	indexTemplate = template.Must(template.New("index").Parse(indexEmbed))
+	indexTemplate  = template.Must(template.New("index").Parse(indexEmbed))
+	configTemplate = template.Must(template.New("config").Parse(configEmbed))
 )
 
 type indexConfig struct {
@@ -32,49 +35,63 @@ type indexConfig struct {
 
 func Run(ctx context.Context, slug string, fsys afero.Fs) error {
 	// 1. Sanity checks.
-	funcDir := filepath.Join(utils.FunctionsDir, slug)
-	{
-		if err := utils.ValidateFunctionSlug(slug); err != nil {
-			return err
-		}
+	if err := utils.ValidateFunctionSlug(slug); err != nil {
+		return err
 	}
-
 	// 2. Create new function.
-	{
-		if err := utils.MkdirIfNotExistFS(fsys, funcDir); err != nil {
-			return err
-		}
-
-		// Load config if available
-		if err := flags.LoadConfig(fsys); err != nil {
-			utils.CmdSuggestion = ""
-		}
-
-		if err := createTemplateFile(fsys, filepath.Join(funcDir, "index.ts"), indexTemplate, indexConfig{
-			URL:   utils.GetApiUrl("/functions/v1/" + slug),
-			Token: utils.Config.Auth.AnonKey,
-		}); err != nil {
-			return errors.Errorf("failed to create function entrypoint: %w", err)
-		}
-
-		if err := afero.WriteFile(fsys, filepath.Join(funcDir, "deno.json"), []byte(denoEmbed), 0644); err != nil {
-			return errors.Errorf("failed to create deno.json config: %w", err)
-		}
-
-		if err := afero.WriteFile(fsys, filepath.Join(funcDir, ".npmrc"), []byte(npmrcEmbed), 0644); err != nil {
-			return errors.Errorf("failed to create .npmrc config: %w", err)
-		}
+	funcDir := filepath.Join(utils.FunctionsDir, slug)
+	if err := utils.MkdirIfNotExistFS(fsys, funcDir); err != nil {
+		return err
+	}
+	// Load config if available
+	if err := flags.LoadConfig(fsys); err != nil {
+		fmt.Fprintln(utils.GetDebugLogger(), err)
+	}
+	if err := createEntrypointFile(slug, fsys); err != nil {
+		return err
+	}
+	if err := appendConfigFile(slug, fsys); err != nil {
+		return err
+	}
+	// 3. Create optional files
+	if err := afero.WriteFile(fsys, filepath.Join(funcDir, "deno.json"), []byte(denoEmbed), 0644); err != nil {
+		return errors.Errorf("failed to create deno.json config: %w", err)
+	}
+	if err := afero.WriteFile(fsys, filepath.Join(funcDir, ".npmrc"), []byte(npmrcEmbed), 0644); err != nil {
+		return errors.Errorf("failed to create .npmrc config: %w", err)
 	}
-
 	fmt.Println("Created new Function at " + utils.Bold(funcDir))
 	return nil
 }
 
-func createTemplateFile(fsys afero.Fs, path string, tmpl *template.Template, data interface{}) error {
-	f, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
+func createEntrypointFile(slug string, fsys afero.Fs) error {
+	entrypointPath := filepath.Join(utils.FunctionsDir, slug, "index.ts")
+	f, err := fsys.OpenFile(entrypointPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
 	if err != nil {
-		return err
+		return errors.Errorf("failed to create entrypoint: %w", err)
+	}
+	defer f.Close()
+	if err := indexTemplate.Option("missingkey=error").Execute(f, indexConfig{
+		URL:   utils.GetApiUrl("/functions/v1/" + slug),
+		Token: utils.Config.Auth.AnonKey,
+	}); err != nil {
+		return errors.Errorf("failed to write entrypoint: %w", err)
+	}
+	return nil
+}
+
+func appendConfigFile(slug string, fsys afero.Fs) error {
+	if _, exists := utils.Config.Functions[slug]; exists {
+		fmt.Fprintf(os.Stderr, "[functions.%s] is already declared in %s\n", slug, utils.Bold(utils.ConfigPath))
+		return nil
+	}
+	f, err := fsys.OpenFile(utils.ConfigPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
+	if err != nil {
+		return errors.Errorf("failed to append config: %w", err)
 	}
 	defer f.Close()
-	return tmpl.Option("missingkey=error").Execute(f, data)
+	if err := configTemplate.Option("missingkey=error").Execute(f, slug); err != nil {
+		return errors.Errorf("failed to append template: %w", err)
+	}
+	return nil
 }
diff --git a/internal/functions/new/templates/config.toml b/internal/functions/new/templates/config.toml
new file mode 100644
index 00000000..9b3b1970
--- /dev/null
+++ b/internal/functions/new/templates/config.toml
@@ -0,0 +1,11 @@
+
+[functions.{{ . }}]
+enabled = true
+verify_jwt = true
+import_map = "./functions/{{ . }}/deno.json"
+# Uncomment to specify a custom file path to the entrypoint.
+# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx
+entrypoint = "./functions/{{ . }}/index.ts"
+# Specifies static files to be bundled with the function. Supports glob patterns.
+# For example, if you want to serve static HTML pages in your function:
+# static_files = [ "./functions/{{ . }}/*.html" ]
diff --git a/internal/functions/serve/serve.go b/internal/functions/serve/serve.go
index 978cb9e7..f46c1af2 100644
--- a/internal/functions/serve/serve.go
+++ b/internal/functions/serve/serve.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"os"
-	"path/filepath"
 	"strconv"
 	"strings"
 
@@ -97,15 +96,7 @@ func Run(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPa
 }
 
 func ServeFunctions(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPath string, dbUrl string, runtimeOption RuntimeOption, fsys afero.Fs) error {
-	// 1. Load default values
-	if envFilePath == "" {
-		if f, err := fsys.Stat(utils.FallbackEnvFilePath); err == nil && !f.IsDir() {
-			envFilePath = utils.FallbackEnvFilePath
-		}
-	} else if !filepath.IsAbs(envFilePath) {
-		envFilePath = filepath.Join(utils.CurrentDirAbs, envFilePath)
-	}
-	// 2. Parse user defined env
+	// 1. Parse custom env file
 	env, err := parseEnvFile(envFilePath, fsys)
 	if err != nil {
 		return err
@@ -124,7 +115,7 @@ func ServeFunctions(ctx context.Context, envFilePath string, noVerifyJWT *bool,
 	if runtimeOption.InspectMode != nil {
 		env = append(env, "SUPABASE_INTERNAL_WALLCLOCK_LIMIT_SEC=0")
 	}
-	// 3. Parse custom import map
+	// 2. Parse custom import map
 	cwd, err := os.Getwd()
 	if err != nil {
 		return errors.Errorf("failed to get working directory: %w", err)
@@ -134,7 +125,7 @@ func ServeFunctions(ctx context.Context, envFilePath string, noVerifyJWT *bool,
 		return err
 	}
 	env = append(env, "SUPABASE_INTERNAL_FUNCTIONS_CONFIG="+functionsConfigString)
-	// 4. Parse entrypoint script
+	// 3. Parse entrypoint script
 	cmd := append([]string{
 		"edge-runtime",
 		"start",
@@ -150,7 +141,7 @@ func ServeFunctions(ctx context.Context, envFilePath string, noVerifyJWT *bool,
 ` + mainFuncEmbed + `
 EOF
 `}
-	// 5. Parse exposed ports
+	// 4. Parse exposed ports
 	dockerRuntimePort := nat.Port(fmt.Sprintf("%d/tcp", dockerRuntimeServerPort))
 	exposedPorts := nat.PortSet{dockerRuntimePort: struct{}{}}
 	portBindings := nat.PortMap{}
@@ -161,7 +152,7 @@ EOF
 			HostPort: strconv.FormatUint(uint64(utils.Config.EdgeRuntime.InspectorPort), 10),
 		}}
 	}
-	// 6. Start container
+	// 5. Start container
 	_, err = utils.DockerStart(
 		ctx,
 		container.Config{
@@ -189,22 +180,17 @@ EOF
 }
 
 func parseEnvFile(envFilePath string, fsys afero.Fs) ([]string, error) {
-	env := []string{}
-	if len(envFilePath) == 0 {
-		return env, nil
-	}
-	envMap, err := set.ParseEnvFile(envFilePath, fsys)
-	if err != nil {
-		return env, err
-	}
-	for name, value := range envMap {
-		if strings.HasPrefix(name, "SUPABASE_") {
-			fmt.Fprintln(os.Stderr, "Env name cannot start with SUPABASE_, skipping: "+name)
-			continue
+	if envFilePath == "" {
+		if f, err := fsys.Stat(utils.FallbackEnvFilePath); err == nil && !f.IsDir() {
+			envFilePath = utils.FallbackEnvFilePath
 		}
-		env = append(env, name+"="+value)
 	}
-	return env, nil
+	env := []string{}
+	secrets, err := set.ListSecrets(envFilePath, fsys)
+	for _, v := range secrets {
+		env = append(env, fmt.Sprintf("%s=%s", v.Name, v.Value))
+	}
+	return env, err
 }
 
 func populatePerFunctionConfigs(cwd, importMapPath string, noVerifyJWT *bool, fsys afero.Fs) ([]string, string, error) {
diff --git a/internal/projects/apiKeys/api_keys.go b/internal/projects/apiKeys/api_keys.go
index 7daddce2..5d5af351 100644
--- a/internal/projects/apiKeys/api_keys.go
+++ b/internal/projects/apiKeys/api_keys.go
@@ -19,7 +19,8 @@ func Run(ctx context.Context, projectRef string, fsys afero.Fs) error {
 		return err
 	}
 
-	if utils.OutputFormat.Value == utils.OutputPretty {
+	switch utils.OutputFormat.Value {
+	case utils.OutputPretty:
 		table := `|NAME|KEY VALUE|
 |-|-|
 `
@@ -28,6 +29,8 @@ func Run(ctx context.Context, projectRef string, fsys afero.Fs) error {
 		}
 
 		return list.RenderTable(table)
+	case utils.OutputToml, utils.OutputEnv:
+		return utils.EncodeOutput(utils.OutputFormat.Value, os.Stdout, ToEnv(keys))
 	}
 
 	return utils.EncodeOutput(utils.OutputFormat.Value, os.Stdout, keys)
@@ -37,9 +40,18 @@ func RunGetApiKeys(ctx context.Context, projectRef string) ([]api.ApiKeyResponse
 	resp, err := utils.GetSupabase().V1GetProjectApiKeysWithResponse(ctx, projectRef, &api.V1GetProjectApiKeysParams{})
 	if err != nil {
 		return nil, errors.Errorf("failed to get api keys: %w", err)
-	}
-	if resp.JSON200 == nil {
-		return nil, errors.New("Unexpected error retrieving project api-keys: " + string(resp.Body))
+	} else if resp.JSON200 == nil {
+		return nil, errors.Errorf("unexpected get api keys status %d: %s", resp.StatusCode(), string(resp.Body))
 	}
 	return *resp.JSON200, nil
 }
+
+func ToEnv(keys []api.ApiKeyResponse) map[string]string {
+	envs := make(map[string]string, len(keys))
+	for _, entry := range keys {
+		name := strings.ToUpper(entry.Name)
+		key := fmt.Sprintf("SUPABASE_%s_KEY", name)
+		envs[key] = entry.ApiKey
+	}
+	return envs
+}
diff --git a/internal/projects/create/create.go b/internal/projects/create/create.go
index ddbd46b4..bfd46176 100644
--- a/internal/projects/create/create.go
+++ b/internal/projects/create/create.go
@@ -36,7 +36,8 @@ func Run(ctx context.Context, params api.V1CreateProjectBodyDto, fsys afero.Fs)
 
 	projectUrl := fmt.Sprintf("%s/project/%s", utils.GetSupabaseDashboardURL(), resp.JSON201.Id)
 	fmt.Fprintf(os.Stderr, "Created a new project %s at %s\n", utils.Aqua(resp.JSON201.Name), utils.Bold(projectUrl))
-	if utils.OutputFormat.Value == utils.OutputPretty {
+	switch utils.OutputFormat.Value {
+	case utils.OutputPretty, utils.OutputEnv:
 		return nil
 	}
 
diff --git a/internal/projects/list/list.go b/internal/projects/list/list.go
index ef356dfc..babded41 100644
--- a/internal/projects/list/list.go
+++ b/internal/projects/list/list.go
@@ -41,7 +41,8 @@ func Run(ctx context.Context, fsys afero.Fs) error {
 		})
 	}
 
-	if utils.OutputFormat.Value == utils.OutputPretty {
+	switch utils.OutputFormat.Value {
+	case utils.OutputPretty:
 		table := `LINKED|ORG ID|REFERENCE ID|NAME|REGION|CREATED AT (UTC)
 |-|-|-|-|-|-|
 `
@@ -57,12 +58,14 @@ func Run(ctx context.Context, fsys afero.Fs) error {
 			)
 		}
 		return list.RenderTable(table)
-	} else if utils.OutputFormat.Value == utils.OutputToml {
+	case utils.OutputToml:
 		return utils.EncodeOutput(utils.OutputFormat.Value, os.Stdout, struct {
 			Projects []linkedProject `toml:"projects"`
 		}{
 			Projects: projects,
 		})
+	case utils.OutputEnv:
+		return errors.Errorf("--output env flag is not supported")
 	}
 
 	return utils.EncodeOutput(utils.OutputFormat.Value, os.Stdout, projects)
diff --git a/internal/secrets/set/set.go b/internal/secrets/set/set.go
index d2877c31..de758e5e 100644
--- a/internal/secrets/set/set.go
+++ b/internal/secrets/set/set.go
@@ -13,61 +13,73 @@ import (
 	"github.com/joho/godotenv"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/utils"
+	"github.com/supabase/cli/internal/utils/flags"
 	"github.com/supabase/cli/pkg/api"
 )
 
 func Run(ctx context.Context, projectRef, envFilePath string, args []string, fsys afero.Fs) error {
 	// 1. Sanity checks.
-	envMap := make(map[string]string, len(args))
+	if err := flags.LoadConfig(fsys); err != nil {
+		fmt.Fprintln(utils.GetDebugLogger(), err)
+	}
+	secrets, err := ListSecrets(envFilePath, fsys, args...)
+	if err != nil {
+		return err
+	}
+	if len(secrets) == 0 {
+		return errors.New("No arguments found. Use --env-file to read from a .env file.")
+	}
+	// 2. Set secret(s).
+	resp, err := utils.GetSupabase().V1BulkCreateSecretsWithResponse(ctx, projectRef, secrets)
+	if err != nil {
+		return errors.Errorf("failed to set secrets: %w", err)
+	} else if resp.StatusCode() != http.StatusCreated {
+		return errors.New("Unexpected error setting project secrets: " + string(resp.Body))
+	}
+	fmt.Println("Finished " + utils.Aqua("supabase secrets set") + ".")
+	return nil
+}
+
+func ListSecrets(envFilePath string, fsys afero.Fs, envArgs ...string) ([]api.CreateSecretBody, error) {
+	envMap := map[string]string{}
+	for name, secret := range utils.Config.EdgeRuntime.Secrets {
+		if len(secret.SHA256) > 0 {
+			envMap[name] = secret.Value
+		}
+	}
 	if len(envFilePath) > 0 {
 		if !filepath.IsAbs(envFilePath) {
 			envFilePath = filepath.Join(utils.CurrentDirAbs, envFilePath)
 		}
-		parsed, err := ParseEnvFile(envFilePath, fsys)
+		parsed, err := parseEnvFile(envFilePath, fsys)
 		if err != nil {
-			return err
+			return nil, err
 		}
 		maps.Copy(envMap, parsed)
 	}
-	for _, pair := range args {
+	for _, pair := range envArgs {
 		name, value, found := strings.Cut(pair, "=")
 		if !found {
-			return errors.Errorf("Invalid secret pair: %s. Must be NAME=VALUE.", pair)
+			return nil, errors.Errorf("Invalid secret pair: %s. Must be NAME=VALUE.", pair)
 		}
 		envMap[name] = value
 	}
-	if len(envMap) == 0 {
-		return errors.New("No arguments found. Use --env-file to read from a .env file.")
-	}
-	// 2. Set secret(s).
-	var secrets api.V1BulkCreateSecretsJSONBody
+	var result []api.CreateSecretBody
 	for name, value := range envMap {
 		// Lower case prefix is accepted by API
 		if strings.HasPrefix(name, "SUPABASE_") {
 			fmt.Fprintln(os.Stderr, "Env name cannot start with SUPABASE_, skipping: "+name)
 			continue
 		}
-		secret := api.CreateSecretBody{
+		result = append(result, api.CreateSecretBody{
 			Name:  name,
 			Value: value,
-		}
-		secrets = append(secrets, secret)
-	}
-
-	resp, err := utils.GetSupabase().V1BulkCreateSecretsWithResponse(ctx, projectRef, secrets)
-	if err != nil {
-		return errors.Errorf("failed to set secrets: %w", err)
-	}
-
-	if resp.StatusCode() != http.StatusCreated {
-		return errors.New("Unexpected error setting project secrets: " + string(resp.Body))
+		})
 	}
-
-	fmt.Println("Finished " + utils.Aqua("supabase secrets set") + ".")
-	return nil
+	return result, nil
 }
 
-func ParseEnvFile(envFilePath string, fsys afero.Fs) (map[string]string, error) {
+func parseEnvFile(envFilePath string, fsys afero.Fs) (map[string]string, error) {
 	f, err := fsys.Open(envFilePath)
 	if err != nil {
 		return nil, errors.Errorf("failed to open env file: %w", err)
diff --git a/internal/sso/create/create.go b/internal/sso/create/create.go
index a33b6e7e..802bbd05 100644
--- a/internal/sso/create/create.go
+++ b/internal/sso/create/create.go
@@ -83,7 +83,8 @@ func Run(ctx context.Context, params RunParams) error {
 			CreatedAt: resp.JSON201.CreatedAt,
 			UpdatedAt: resp.JSON201.UpdatedAt,
 		})
-
+	case utils.OutputEnv:
+		return nil
 	default:
 		return utils.EncodeOutput(params.Format, os.Stdout, resp.JSON201)
 	}
diff --git a/internal/sso/get/get.go b/internal/sso/get/get.go
index 34370523..75de6abc 100644
--- a/internal/sso/get/get.go
+++ b/internal/sso/get/get.go
@@ -30,7 +30,6 @@ func Run(ctx context.Context, ref, providerId, format string) error {
 	case utils.OutputMetadata:
 		_, err := fmt.Println(*resp.JSON200.Saml.MetadataXml)
 		return err
-
 	case utils.OutputPretty:
 		return render.SingleMarkdown(api.Provider{
 			Id:        resp.JSON200.Id,
@@ -39,7 +38,8 @@ func Run(ctx context.Context, ref, providerId, format string) error {
 			CreatedAt: resp.JSON200.CreatedAt,
 			UpdatedAt: resp.JSON200.UpdatedAt,
 		})
-
+	case utils.OutputEnv:
+		return errors.Errorf("--output env flag is not supported")
 	default:
 		return utils.EncodeOutput(format, os.Stdout, resp.JSON200)
 	}
diff --git a/internal/sso/info/info.go b/internal/sso/info/info.go
index c8a25805..0de8f1ef 100644
--- a/internal/sso/info/info.go
+++ b/internal/sso/info/info.go
@@ -13,9 +13,8 @@ func Run(ctx context.Context, ref string, format string) error {
 	switch format {
 	case utils.OutputPretty:
 		return render.InfoMarkdown(ref)
-
 	default:
-		return utils.EncodeOutput(format, os.Stdout, map[string]interface{}{
+		return utils.EncodeOutput(format, os.Stdout, map[string]string{
 			"acs_url":     fmt.Sprintf("https://%s.supabase.co/auth/v1/sso/saml/acs", ref),
 			"entity_id":   fmt.Sprintf("https://%s.supabase.co/auth/v1/sso/saml/metadata", ref),
 			"relay_state": fmt.Sprintf("https://%s.supabase.co", ref),
diff --git a/internal/sso/remove/remove.go b/internal/sso/remove/remove.go
index 1514314d..69bf5c4d 100644
--- a/internal/sso/remove/remove.go
+++ b/internal/sso/remove/remove.go
@@ -34,7 +34,8 @@ func Run(ctx context.Context, ref, providerId, format string) error {
 			CreatedAt: resp.JSON200.CreatedAt,
 			UpdatedAt: resp.JSON200.UpdatedAt,
 		})
-
+	case utils.OutputEnv:
+		return nil
 	default:
 		return utils.EncodeOutput(format, os.Stdout, resp.JSON200)
 	}
diff --git a/internal/sso/update/update.go b/internal/sso/update/update.go
index e8a745dc..5729a64e 100644
--- a/internal/sso/update/update.go
+++ b/internal/sso/update/update.go
@@ -119,7 +119,8 @@ func Run(ctx context.Context, params RunParams) error {
 			CreatedAt: putResp.JSON200.CreatedAt,
 			UpdatedAt: putResp.JSON200.UpdatedAt,
 		})
-
+	case utils.OutputEnv:
+		return nil
 	default:
 		return utils.EncodeOutput(params.Format, os.Stdout, putResp.JSON200)
 	}
diff --git a/internal/start/start.go b/internal/start/start.go
index 5402283b..7a00c1dd 100644
--- a/internal/start/start.go
+++ b/internal/start/start.go
@@ -504,6 +504,14 @@ EOF
 			fmt.Sprintf("GOTRUE_MFA_WEB_AUTHN_ENROLL_ENABLED=%v", utils.Config.Auth.MFA.WebAuthn.EnrollEnabled),
 			fmt.Sprintf("GOTRUE_MFA_WEB_AUTHN_VERIFY_ENABLED=%v", utils.Config.Auth.MFA.WebAuthn.VerifyEnabled),
 			fmt.Sprintf("GOTRUE_MFA_MAX_ENROLLED_FACTORS=%v", utils.Config.Auth.MFA.MaxEnrolledFactors),
+
+			// Add rate limit configurations
+			fmt.Sprintf("GOTRUE_RATE_LIMIT_ANONYMOUS_USERS=%v", utils.Config.Auth.RateLimit.AnonymousUsers),
+			fmt.Sprintf("GOTRUE_RATE_LIMIT_TOKEN_REFRESH=%v", utils.Config.Auth.RateLimit.TokenRefresh),
+			fmt.Sprintf("GOTRUE_RATE_LIMIT_OTP=%v", utils.Config.Auth.RateLimit.SignInSignUps),
+			fmt.Sprintf("GOTRUE_RATE_LIMIT_VERIFY=%v", utils.Config.Auth.RateLimit.TokenVerifications),
+			fmt.Sprintf("GOTRUE_RATE_LIMIT_SMS_SENT=%v", utils.Config.Auth.RateLimit.SmsSent),
+			fmt.Sprintf("GOTRUE_RATE_LIMIT_EMAIL_SENT=%v", utils.Config.Auth.RateLimit.EmailSent),
 		}
 
 		if utils.Config.Auth.Email.Smtp != nil && utils.Config.Auth.Email.Smtp.Enabled {
@@ -518,7 +526,7 @@ EOF
 		} else if utils.Config.Inbucket.Enabled {
 			env = append(env,
 				"GOTRUE_SMTP_HOST="+utils.InbucketId,
-				"GOTRUE_SMTP_PORT=2500",
+				"GOTRUE_SMTP_PORT=1025",
 				fmt.Sprintf("GOTRUE_SMTP_ADMIN_EMAIL=%s", utils.Config.Inbucket.AdminEmail),
 				fmt.Sprintf("GOTRUE_SMTP_SENDER_NAME=%s", utils.Config.Inbucket.SenderName),
 			)
@@ -700,14 +708,14 @@ EOF
 		started = append(started, utils.GotrueId)
 	}
 
-	// Start Inbucket.
+	// Start Mailpit
 	if utils.Config.Inbucket.Enabled && !isContainerExcluded(utils.Config.Inbucket.Image, excluded) {
-		inbucketPortBindings := nat.PortMap{"9000/tcp": []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Inbucket.Port), 10)}}}
+		inbucketPortBindings := nat.PortMap{"8025/tcp": []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Inbucket.Port), 10)}}}
 		if utils.Config.Inbucket.SmtpPort != 0 {
-			inbucketPortBindings["2500/tcp"] = []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Inbucket.SmtpPort), 10)}}
+			inbucketPortBindings["1025/tcp"] = []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Inbucket.SmtpPort), 10)}}
 		}
 		if utils.Config.Inbucket.Pop3Port != 0 {
-			inbucketPortBindings["1100/tcp"] = []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Inbucket.Pop3Port), 10)}}
+			inbucketPortBindings["1110/tcp"] = []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Inbucket.Pop3Port), 10)}}
 		}
 		if _, err := utils.DockerStart(
 			ctx,
@@ -715,12 +723,6 @@ EOF
 				Image: utils.Config.Inbucket.Image,
 			},
 			container.HostConfig{
-				Binds: []string{
-					// Override default mount points to avoid creating multiple anonymous volumes
-					// Ref: https://github.com/inbucket/inbucket/blob/v3.0.4/Dockerfile#L52
-					utils.InbucketId + ":/config",
-					utils.InbucketId + ":/storage",
-				},
 				PortBindings:  inbucketPortBindings,
 				RestartPolicy: container.RestartPolicy{Name: "always"},
 			},
@@ -988,7 +990,7 @@ EOF
 					"SUPABASE_ANON_KEY=" + utils.Config.Auth.AnonKey,
 					"SUPABASE_SERVICE_KEY=" + utils.Config.Auth.ServiceRoleKey,
 					"LOGFLARE_API_KEY=" + utils.Config.Analytics.ApiKey,
-					"OPENAI_API_KEY=" + utils.Config.Studio.OpenaiApiKey,
+					"OPENAI_API_KEY=" + utils.Config.Studio.OpenaiApiKey.Value,
 					fmt.Sprintf("LOGFLARE_URL=http://%v:4000", utils.LogflareId),
 					fmt.Sprintf("NEXT_PUBLIC_ENABLE_LOGS=%v", utils.Config.Analytics.Enabled),
 					fmt.Sprintf("NEXT_ANALYTICS_BACKEND_PROVIDER=%v", utils.Config.Analytics.Backend),
diff --git a/internal/utils/connect.go b/internal/utils/connect.go
index 9479d4e4..c7c4124e 100644
--- a/internal/utils/connect.go
+++ b/internal/utils/connect.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/go-errors/errors"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/viper"
@@ -47,13 +48,9 @@ func GetPoolerConfig(projectRef string) *pgconn.Config {
 		fmt.Fprintln(logger, "Pooler URL is not configured")
 		return nil
 	}
-	// Remove password from pooler connection string because the placeholder text
-	// [YOUR-PASSWORD] messes up pgconn.ParseConfig. The password must be percent
-	// escaped so we cannot simply call strings.Replace with actual password.
-	poolerUrl := strings.ReplaceAll(Config.Db.Pooler.ConnectionString, "[YOUR-PASSWORD]", "")
-	poolerConfig, err := pgconn.ParseConfig(poolerUrl)
+	poolerConfig, err := ParsePoolerURL(Config.Db.Pooler.ConnectionString)
 	if err != nil {
-		fmt.Fprintln(logger, "Failed to parse pooler URL:", poolerUrl)
+		fmt.Fprintln(logger, err)
 		return nil
 	}
 	if poolerConfig.RuntimeParams == nil {
@@ -77,12 +74,24 @@ func GetPoolerConfig(projectRef string) *pgconn.Config {
 		fmt.Fprintln(logger, "Pooler hostname does not belong to Supabase domain:", poolerConfig.Host)
 		return nil
 	}
-	fmt.Fprintln(logger, "Using connection pooler:", poolerUrl)
+	fmt.Fprintln(logger, "Using connection pooler:", Config.Db.Pooler.ConnectionString)
 	// Supavisor transaction mode does not support prepared statement
 	poolerConfig.Port = 5432
 	return poolerConfig
 }
 
+func ParsePoolerURL(connString string) (*pgconn.Config, error) {
+	// Remove password from pooler connection string because the placeholder text
+	// [YOUR-PASSWORD] messes up pgconn.ParseConfig. The password must be percent
+	// escaped so we cannot simply call strings.Replace with actual password.
+	poolerUrl := strings.ReplaceAll(connString, "[YOUR-PASSWORD]", "")
+	poolerConfig, err := pgconn.ParseConfig(poolerUrl)
+	if err != nil {
+		return nil, errors.Errorf("failed to parse pooler URL: %w", err)
+	}
+	return poolerConfig, nil
+}
+
 func isSupabaseDomain(host string) bool {
 	switch GetSupabaseAPIHost() {
 	case "https://api.supabase.green":
diff --git a/internal/utils/docker.go b/internal/utils/docker.go
index 84911d07..792ffb3d 100644
--- a/internal/utils/docker.go
+++ b/internal/utils/docker.go
@@ -239,7 +239,7 @@ func DockerImagePullWithRetry(ctx context.Context, image string, retries int) er
 
 func DockerPullImageIfNotCached(ctx context.Context, imageName string) error {
 	imageUrl := GetRegistryImageUrl(imageName)
-	if _, _, err := Docker.ImageInspectWithRaw(ctx, imageUrl); err == nil {
+	if _, err := Docker.ImageInspect(ctx, imageUrl); err == nil {
 		return nil
 	} else if !client.IsErrNotFound(err) {
 		return errors.Errorf("failed to inspect docker image: %w", err)
diff --git a/internal/utils/output.go b/internal/utils/output.go
index 6d1a72aa..b518567e 100644
--- a/internal/utils/output.go
+++ b/internal/utils/output.go
@@ -22,19 +22,16 @@ const (
 	OutputMetadata = "metadata"
 )
 
-var (
-	OutputDefaultAllowed = []string{
+var OutputFormat = EnumFlag{
+	Allowed: []string{
+		OutputEnv,
 		OutputPretty,
 		OutputJson,
 		OutputToml,
 		OutputYaml,
-	}
-
-	OutputFormat = EnumFlag{
-		Allowed: OutputDefaultAllowed,
-		Value:   OutputPretty,
-	}
-)
+	},
+	Value: OutputPretty,
+}
 
 func EncodeOutput(format string, w io.Writer, value any) error {
 	switch format {
diff --git a/pkg/config/auth.go b/pkg/config/auth.go
index 30e00e24..dcbd0f19 100644
--- a/pkg/config/auth.go
+++ b/pkg/config/auth.go
@@ -83,13 +83,14 @@ type (
 		MinimumPasswordLength      uint                 `toml:"minimum_password_length"`
 		PasswordRequirements       PasswordRequirements `toml:"password_requirements"`
 
-		Captcha  *captcha `toml:"captcha"`
-		Hook     hook     `toml:"hook"`
-		MFA      mfa      `toml:"mfa"`
-		Sessions sessions `toml:"sessions"`
-		Email    email    `toml:"email"`
-		Sms      sms      `toml:"sms"`
-		External external `toml:"external"`
+		RateLimit rateLimit `toml:"rate_limit"`
+		Captcha   *captcha  `toml:"captcha"`
+		Hook      hook      `toml:"hook"`
+		MFA       mfa       `toml:"mfa"`
+		Sessions  sessions  `toml:"sessions"`
+		Email     email     `toml:"email"`
+		Sms       sms       `toml:"sms"`
+		External  external  `toml:"external"`
 
 		// Custom secrets can be injected from .env file
 		JwtSecret      string `toml:"-" mapstructure:"jwt_secret"`
@@ -105,6 +106,16 @@ type (
 		Firebase tpaFirebase `toml:"firebase"`
 		Auth0    tpaAuth0    `toml:"auth0"`
 		Cognito  tpaCognito  `toml:"aws_cognito"`
+		Clerk    tpaClerk    `toml:"clerk"`
+	}
+
+	rateLimit struct {
+		AnonymousUsers     uint `toml:"anonymous_users"`
+		TokenRefresh       uint `toml:"token_refresh"`
+		SignInSignUps      uint `toml:"sign_in_sign_ups"`
+		TokenVerifications uint `toml:"token_verifications"`
+		EmailSent          uint `toml:"email_sent"`
+		SmsSent            uint `toml:"sms_sent"`
 	}
 
 	tpaFirebase struct {
@@ -127,6 +138,12 @@ type (
 		UserPoolRegion string `toml:"user_pool_region"`
 	}
 
+	tpaClerk struct {
+		Enabled bool `toml:"enabled"`
+
+		Domain string `toml:"domain"`
+	}
+
 	email struct {
 		EnableSignup         bool                     `toml:"enable_signup"`
 		DoubleConfirmChanges bool                     `toml:"double_confirm_changes"`
@@ -262,6 +279,11 @@ func (a *auth) ToUpdateAuthConfigBody() v1API.UpdateAuthConfigBody {
 		PasswordMinLength:                 cast.UintToIntPtr(&a.MinimumPasswordLength),
 		PasswordRequiredCharacters:        cast.Ptr(a.PasswordRequirements.ToChar()),
 	}
+	// Add rate limit fields
+	a.RateLimit.toAuthConfigBody(&body)
+	if s := a.Email.Smtp; s != nil && s.Enabled {
+		body.RateLimitEmailSent = cast.Ptr(cast.UintToInt(a.RateLimit.EmailSent))
+	}
 	// When local config is not set, we assume platform defaults should not change
 	if a.Captcha != nil {
 		a.Captcha.toAuthConfigBody(&body)
@@ -287,6 +309,10 @@ func (a *auth) FromRemoteAuthConfig(remoteConfig v1API.AuthConfigResponse) {
 	a.MinimumPasswordLength = cast.IntToUint(cast.Val(remoteConfig.PasswordMinLength, 0))
 	prc := cast.Val(remoteConfig.PasswordRequiredCharacters, "")
 	a.PasswordRequirements = NewPasswordRequirement(v1API.UpdateAuthConfigBodyPasswordRequiredCharacters(prc))
+	a.RateLimit.fromAuthConfig(remoteConfig)
+	if s := a.Email.Smtp; s != nil && s.Enabled {
+		a.RateLimit.EmailSent = cast.IntToUint(cast.Val(remoteConfig.RateLimitEmailSent, 0))
+	}
 	a.Captcha.fromAuthConfig(remoteConfig)
 	a.Hook.fromAuthConfig(remoteConfig)
 	a.MFA.fromAuthConfig(remoteConfig)
@@ -296,6 +322,24 @@ func (a *auth) FromRemoteAuthConfig(remoteConfig v1API.AuthConfigResponse) {
 	a.External.fromAuthConfig(remoteConfig)
 }
 
+func (r rateLimit) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
+	body.RateLimitAnonymousUsers = cast.Ptr(cast.UintToInt(r.AnonymousUsers))
+	body.RateLimitTokenRefresh = cast.Ptr(cast.UintToInt(r.TokenRefresh))
+	body.RateLimitOtp = cast.Ptr(cast.UintToInt(r.SignInSignUps))
+	body.RateLimitVerify = cast.Ptr(cast.UintToInt(r.TokenVerifications))
+	// Email rate limit is only updated when SMTP is enabled
+	body.RateLimitSmsSent = cast.Ptr(cast.UintToInt(r.SmsSent))
+}
+
+func (r *rateLimit) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
+	r.AnonymousUsers = cast.IntToUint(cast.Val(remoteConfig.RateLimitAnonymousUsers, 0))
+	r.TokenRefresh = cast.IntToUint(cast.Val(remoteConfig.RateLimitTokenRefresh, 0))
+	r.SignInSignUps = cast.IntToUint(cast.Val(remoteConfig.RateLimitOtp, 0))
+	r.TokenVerifications = cast.IntToUint(cast.Val(remoteConfig.RateLimitVerify, 0))
+	// Email rate limit is only updated when SMTP is enabled
+	r.SmsSent = cast.IntToUint(cast.Val(remoteConfig.RateLimitSmsSent, 0))
+}
+
 func (c captcha) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
 	if body.SecurityCaptchaEnabled = &c.Enabled; c.Enabled {
 		body.SecurityCaptchaProvider = cast.Ptr(string(c.Provider))
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 30625680..899deb63 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -205,7 +205,7 @@ type (
 		Image        string `toml:"-"`
 		Port         uint16 `toml:"port"`
 		ApiUrl       string `toml:"api_url"`
-		OpenaiApiKey string `toml:"openai_api_key"`
+		OpenaiApiKey Secret `toml:"openai_api_key"`
 		PgmetaImage  string `toml:"-"`
 	}
 
@@ -224,8 +224,10 @@ type (
 		Image         string        `toml:"-"`
 		Policy        RequestPolicy `toml:"policy"`
 		InspectorPort uint16        `toml:"inspector_port"`
+		Secrets       SecretsConfig `toml:"secrets"`
 	}
 
+	SecretsConfig  map[string]Secret
 	FunctionConfig map[string]function
 
 	function struct {
@@ -312,7 +314,9 @@ func (s *storage) Clone() storage {
 
 func (c *baseConfig) Clone() baseConfig {
 	copy := *c
+	copy.Db.Vault = maps.Clone(c.Db.Vault)
 	copy.Storage = c.Storage.Clone()
+	copy.EdgeRuntime.Secrets = maps.Clone(c.EdgeRuntime.Secrets)
 	copy.Functions = maps.Clone(c.Functions)
 	copy.Auth = c.Auth.Clone()
 	if c.Experimental.Webhooks != nil {
@@ -506,6 +510,12 @@ func (c *config) loadFromReader(v *viper.Viper, r io.Reader) error {
 	}); err != nil {
 		return errors.Errorf("failed to parse config: %w", err)
 	}
+	// Convert keys to upper case: https://github.com/spf13/viper/issues/1014
+	secrets := make(SecretsConfig, len(c.EdgeRuntime.Secrets))
+	for k, v := range c.EdgeRuntime.Secrets {
+		secrets[strings.ToUpper(k)] = v
+	}
+	c.EdgeRuntime.Secrets = secrets
 	return nil
 }
 
@@ -871,7 +881,7 @@ func loadNestedEnv(basePath string) error {
 	if !filepath.IsAbs(basePath) {
 		basePath = filepath.Join(repoDir, basePath)
 	}
-	env := viper.GetString("ENV")
+	env := os.Getenv("SUPABASE_ENV")
 	for cwd := basePath; cwd != filepath.Dir(repoDir); cwd = filepath.Dir(cwd) {
 		if err := os.Chdir(cwd); err != nil && !errors.Is(err, os.ErrNotExist) {
 			return errors.Errorf("failed to change directory: %w", err)
@@ -1199,6 +1209,26 @@ func (c *tpaCognito) validate() (err error) {
 	return nil
 }
 
+var clerkDomainPattern = regexp.MustCompile("^(clerk([.][a-z0-9-]+){2,}|([a-z0-9-]+[.])+clerk[.]accounts[.]dev)$")
+
+func (c *tpaClerk) issuerURL() string {
+	return fmt.Sprintf("https://%s", c.Domain)
+}
+
+func (c *tpaClerk) validate() (err error) {
+	if c.Domain == "" {
+		return errors.New("Invalid config: auth.third_party.clerk is enabled but without a domain.")
+	} else if err := assertEnvLoaded(c.Domain); err != nil {
+		return err
+	}
+
+	if !clerkDomainPattern.MatchString(c.Domain) {
+		return errors.New("Invalid config: auth.third_party.clerk has invalid domain, it usually is like clerk.example.com or example.clerk.accounts.dev. Check https://clerk.com/setup/supabase on how to find the correct value.")
+	}
+
+	return nil
+}
+
 func (tpa *thirdParty) validate() error {
 	enabled := 0
 
@@ -1226,6 +1256,14 @@ func (tpa *thirdParty) validate() error {
 		}
 	}
 
+	if tpa.Clerk.Enabled {
+		enabled += 1
+
+		if err := tpa.Clerk.validate(); err != nil {
+			return err
+		}
+	}
+
 	if enabled > 1 {
 		return errors.New("Invalid config: Only one third_party provider allowed to be enabled at a time.")
 	}
@@ -1246,6 +1284,10 @@ func (tpa *thirdParty) IssuerURL() string {
 		return tpa.Cognito.issuerURL()
 	}
 
+	if tpa.Clerk.Enabled {
+		return tpa.Clerk.issuerURL()
+	}
+
 	return ""
 }
 
diff --git a/pkg/config/constants.go b/pkg/config/constants.go
index b659ba6f..8cee0c39 100644
--- a/pkg/config/constants.go
+++ b/pkg/config/constants.go
@@ -17,7 +17,7 @@ type images struct {
 	Pg15 string `mapstructure:"pg15"`
 	// Append to Services when adding new dependencies below
 	Kong        string `mapstructure:"kong"`
-	Inbucket    string `mapstructure:"inbucket"`
+	Inbucket    string `mapstructure:"mailpit"`
 	Postgrest   string `mapstructure:"postgrest"`
 	Pgmeta      string `mapstructure:"pgmeta"`
 	Studio      string `mapstructure:"studio"`
diff --git a/pkg/config/secret.go b/pkg/config/secret.go
index d83ad536..c78a5b8b 100644
--- a/pkg/config/secret.go
+++ b/pkg/config/secret.go
@@ -27,14 +27,31 @@ func (s Secret) MarshalText() (text []byte, err error) {
 
 const ENCRYPTED_PREFIX = "encrypted:"
 
+func (s *Secret) Decrypt(keys []string) error {
+	if !strings.HasPrefix(s.Value, ENCRYPTED_PREFIX) {
+		return nil
+	}
+	if len(keys) == 0 {
+		return errors.New(errMissingKey)
+	}
+	var err error
+	for _, k := range keys {
+		// Use the first private key that successfully decrypts the secret
+		if s.Value, err = decrypt(k, s.Value); err == nil {
+			break
+		}
+	}
+	// If we didn't break, none of the keys worked
+	return err
+}
+
+var errMissingKey = errors.New("missing private key")
+
 // Decrypt secret values following dotenvx convention:
 // https://github.com/dotenvx/dotenvx/blob/main/src/lib/helpers/decryptKeyValue.js
 func decrypt(key, value string) (string, error) {
-	if !strings.HasPrefix(value, ENCRYPTED_PREFIX) {
-		return value, nil
-	}
 	if len(key) == 0 {
-		return value, errors.New("missing private key")
+		return value, errors.New(errMissingKey)
 	}
 	// Verify private key exists
 	privateKey, err := ecies.NewPrivateKeyFromHex(key)
@@ -55,7 +72,17 @@ func decrypt(key, value string) (string, error) {
 	return string(plaintext), nil
 }
 
+const PRIVATE_KEY_PREFIX = "DOTENV_PRIVATE_KEY"
+
 func DecryptSecretHookFunc(hashKey string) mapstructure.DecodeHookFunc {
+	// Get all env vars and filter for private keys
+	var privateKeys []string
+	for _, env := range os.Environ() {
+		kv := strings.SplitN(env, "=", 2)
+		if kv[0] == PRIVATE_KEY_PREFIX || strings.HasPrefix(kv[0], PRIVATE_KEY_PREFIX+"_") {
+			privateKeys = append(privateKeys, strToArr(kv[1])...)
+		}
+	}
 	return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
 		if f.Kind() != reflect.String {
 			return data, nil
@@ -64,35 +91,19 @@ func DecryptSecretHookFunc(hashKey string) mapstructure.DecodeHookFunc {
 		if t != reflect.TypeOf(result) {
 			return data, nil
 		}
-		value := data.(string)
-		if len(value) == 0 {
+		result.Value = data.(string)
+		if len(result.Value) == 0 {
 			return result, nil
 		}
-		// Get all env vars and filter for DOTENV_PRIVATE_KEY
-		var privateKeys []string
-		for _, env := range os.Environ() {
-			key := strings.Split(env, "=")[0]
-			if key == "DOTENV_PRIVATE_KEY" || strings.HasPrefix(key, "DOTENV_PRIVATE_KEY_") {
-				if value := os.Getenv(key); value != "" {
-					privateKeys = append(privateKeys, value)
-				}
-			}
+		// Unloaded env() references should be returned verbatim without hashing
+		if envPattern.MatchString(result.Value) {
+			return result, nil
 		}
-		// Try each private key
-		var err error
-		privKey := strings.Join(privateKeys, ",")
-		for _, k := range strings.Split(privKey, ",") {
-			// Use the first private key that successfully decrypts the secret
-			if result.Value, err = decrypt(k, value); err == nil {
-				// Unloaded env() references may be returned verbatim.
-				// Don't hash those values as they are meaningless.
-				if !envPattern.MatchString(result.Value) {
-					result.SHA256 = sha256Hmac(hashKey, result.Value)
-				}
-				break
-			}
+		if err := result.Decrypt(privateKeys); err != nil {
+			return result, err
 		}
-		// If we get here, none of the keys worked
-		return result, err
+		// Decrypted values should be hashed
+		result.SHA256 = sha256Hmac(hashKey, result.Value)
+		return result, nil
 	}
 }
diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile
index 124332e8..65e002e5 100644
--- a/pkg/config/templates/Dockerfile
+++ b/pkg/config/templates/Dockerfile
@@ -1,19 +1,19 @@
 # Exposed for updates by .github/dependabot.yml
-FROM supabase/postgres:15.8.1.044 AS pg15
+FROM supabase/postgres:15.8.1.048 AS pg15
 # Append to ServiceImages when adding new dependencies below
 FROM library/kong:2.8.1 AS kong
-FROM inbucket/inbucket:3.0.3 AS inbucket
+FROM axllent/mailpit:v1.22.3 AS mailpit
 FROM postgrest/postgrest:v12.2.8 AS postgrest
-FROM supabase/postgres-meta:v0.86.0 AS pgmeta
+FROM supabase/postgres-meta:v0.86.1 AS pgmeta
 FROM supabase/studio:20250224-d10db0f AS studio
 FROM darthsim/imgproxy:v3.8.0 AS imgproxy
 FROM supabase/edge-runtime:v1.67.2 AS edgeruntime
 FROM timberio/vector:0.28.1-alpine AS vector
-FROM supabase/supavisor:2.3.9 AS supavisor
-FROM supabase/gotrue:v2.169.0 AS gotrue
-FROM supabase/realtime:v2.34.31 AS realtime
-FROM supabase/storage-api:v1.19.1 AS storage
-FROM supabase/logflare:1.11.0 AS logflare
+FROM supabase/supavisor:2.4.12 AS supavisor
+FROM supabase/gotrue:v2.170.0 AS gotrue
+FROM supabase/realtime:v2.34.40 AS realtime
+FROM supabase/storage-api:v1.19.2 AS storage
+FROM supabase/logflare:1.12.5 AS logflare
 # Append to JobImages when adding new dependencies below
 FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ
 FROM supabase/migra:3.0.1663481299 AS migra
diff --git a/pkg/config/templates/config.toml b/pkg/config/templates/config.toml
index 842be712..ec363cd4 100644
--- a/pkg/config/templates/config.toml
+++ b/pkg/config/templates/config.toml
@@ -127,6 +127,21 @@ minimum_password_length = 6
 # are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols`
 password_requirements = ""
 
+[auth.rate_limit]
+# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled.
+email_sent = 2
+# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled.
+sms_sent = 30
+# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true.
+anonymous_users = 30
+# Number of sessions that can be refreshed in a 5 minute interval per IP address.
+token_refresh = 150
+# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users).
+sign_in_sign_ups = 30
+# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address.
+token_verifications = 30
+
+
 # Configure one of the supported captcha providers: `hcaptcha`, `turnstile`.
 # [auth.captcha]
 # enabled = true
@@ -255,6 +270,12 @@ enabled = false
 # user_pool_id = "my-user-pool-id"
 # user_pool_region = "us-east-1"
 
+# Use Clerk as a third-party provider alongside Supabase Auth.
+[auth.third_party.clerk]
+enabled = false
+# Obtain from https://clerk.com/setup/supabase
+# domain = "example.clerk.accounts.dev"
+
 [edge_runtime]
 enabled = true
 # Configure one of the supported request policies: `oneshot`, `per_worker`.
@@ -263,17 +284,8 @@ policy = "oneshot"
 # Port to attach the Chrome inspector for debugging edge functions.
 inspector_port = 8083
 
-# Use these configurations to customize your Edge Function.
-# [functions.MY_FUNCTION_NAME]
-# enabled = true
-# verify_jwt = true
-# import_map = "./functions/MY_FUNCTION_NAME/deno.json"
-# Uncomment to specify a custom file path to the entrypoint.
-# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx
-# entrypoint = "./functions/MY_FUNCTION_NAME/index.ts"
-# Specifies static files to be bundled with the function. Supports glob patterns.
-# For example, if you want to serve static HTML pages in your function:
-# static_files = [ "./functions/MY_FUNCTION_NAME/*.html" ]
+# [edge_runtime.secrets]
+# secret_key = "env(SECRET_VALUE)"
 
 [analytics]
 enabled = true
diff --git a/pkg/config/utils.go b/pkg/config/utils.go
index 78a991c8..e4a77aef 100644
--- a/pkg/config/utils.go
+++ b/pkg/config/utils.go
@@ -89,7 +89,7 @@ func replaceImageTag(image string, tag string) string {
 func strToArr(v string) []string {
 	// Avoid returning [""] if v is empty
 	if len(v) == 0 {
-		return nil
+		return []string{}
 	}
 	return strings.Split(v, ",")
 }
diff --git a/pkg/function/api.go b/pkg/function/api.go
index 9fdf52b5..cdf530b5 100644
--- a/pkg/function/api.go
+++ b/pkg/function/api.go
@@ -11,12 +11,34 @@ type EdgeRuntimeAPI struct {
 	project string
 	client  api.ClientWithResponses
 	eszip   EszipBundler
+	maxJobs uint
 }
 
 type EszipBundler interface {
 	Bundle(ctx context.Context, entrypoint string, importMap string, staticFiles []string, output io.Writer) error
 }
 
-func NewEdgeRuntimeAPI(project string, client api.ClientWithResponses, bundler EszipBundler) EdgeRuntimeAPI {
-	return EdgeRuntimeAPI{client: client, project: project, eszip: bundler}
+func NewEdgeRuntimeAPI(project string, client api.ClientWithResponses, opts ...withOption) EdgeRuntimeAPI {
+	result := EdgeRuntimeAPI{client: client, project: project}
+	for _, apply := range opts {
+		apply(&result)
+	}
+	if result.maxJobs == 0 {
+		result.maxJobs = 1
+	}
+	return result
+}
+
+type withOption func(*EdgeRuntimeAPI)
+
+func WithBundler(bundler EszipBundler) withOption {
+	return func(era *EdgeRuntimeAPI) {
+		era.eszip = bundler
+	}
+}
+
+func WithMaxJobs(maxJobs uint) withOption {
+	return func(era *EdgeRuntimeAPI) {
+		era.maxJobs = maxJobs
+	}
 }
diff --git a/pkg/function/batch.go b/pkg/function/batch.go
index 739d8f84..9c0f8205 100644
--- a/pkg/function/batch.go
+++ b/pkg/function/batch.go
@@ -34,6 +34,8 @@ func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig con
 	for _, f := range result {
 		exists[f.Slug] = struct{}{}
 	}
+	toUpdate := map[string]api.BulkUpdateFunctionBody{}
+OUTER:
 	for slug, function := range functionConfig {
 		if !function.Enabled {
 			fmt.Fprintln(os.Stderr, "Skipped deploying Function:", slug)
@@ -41,7 +43,7 @@ func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig con
 		}
 		for _, keep := range filter {
 			if !keep(slug) {
-				continue
+				continue OUTER
 			}
 		}
 		var body bytes.Buffer
@@ -51,27 +53,53 @@ func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig con
 		// Update if function already exists
 		upsert := func() error {
 			if _, ok := exists[slug]; ok {
-				if resp, err := s.client.V1UpdateAFunctionWithBodyWithResponse(ctx, s.project, slug, &api.V1UpdateAFunctionParams{
+				resp, err := s.client.V1UpdateAFunctionWithBodyWithResponse(ctx, s.project, slug, &api.V1UpdateAFunctionParams{
 					VerifyJwt:      &function.VerifyJWT,
 					ImportMapPath:  toFileURL(function.ImportMap),
 					EntrypointPath: toFileURL(function.Entrypoint),
-				}, eszipContentType, bytes.NewReader(body.Bytes())); err != nil {
+				}, eszipContentType, bytes.NewReader(body.Bytes()))
+				if err != nil {
 					return errors.Errorf("failed to update function: %w", err)
 				} else if resp.JSON200 == nil {
 					return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body))
 				}
+				toUpdate[slug] = api.BulkUpdateFunctionBody{
+					Id:             resp.JSON200.Id,
+					Name:           resp.JSON200.Name,
+					Slug:           resp.JSON200.Slug,
+					Version:        resp.JSON200.Version,
+					EntrypointPath: resp.JSON200.EntrypointPath,
+					ImportMap:      resp.JSON200.ImportMap,
+					ImportMapPath:  resp.JSON200.ImportMapPath,
+					VerifyJwt:      resp.JSON200.VerifyJwt,
+					Status:         api.BulkUpdateFunctionBodyStatus(resp.JSON200.Status),
+					CreatedAt:      &resp.JSON200.CreatedAt,
+				}
 			} else {
-				if resp, err := s.client.V1CreateAFunctionWithBodyWithResponse(ctx, s.project, &api.V1CreateAFunctionParams{
+				resp, err := s.client.V1CreateAFunctionWithBodyWithResponse(ctx, s.project, &api.V1CreateAFunctionParams{
 					Slug:           &slug,
 					Name:           &slug,
 					VerifyJwt:      &function.VerifyJWT,
 					ImportMapPath:  toFileURL(function.ImportMap),
 					EntrypointPath: toFileURL(function.Entrypoint),
-				}, eszipContentType, bytes.NewReader(body.Bytes())); err != nil {
+				}, eszipContentType, bytes.NewReader(body.Bytes()))
+				if err != nil {
 					return errors.Errorf("failed to create function: %w", err)
 				} else if resp.JSON201 == nil {
 					return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body))
 				}
+				toUpdate[slug] = api.BulkUpdateFunctionBody{
+					Id:             resp.JSON201.Id,
+					Name:           resp.JSON201.Name,
+					Slug:           resp.JSON201.Slug,
+					Version:        resp.JSON201.Version,
+					EntrypointPath: resp.JSON201.EntrypointPath,
+					ImportMap:      resp.JSON201.ImportMap,
+					ImportMapPath:  resp.JSON201.ImportMapPath,
+					VerifyJwt:      resp.JSON201.VerifyJwt,
+					Status:         api.BulkUpdateFunctionBodyStatus(resp.JSON201.Status),
+					CreatedAt:      &resp.JSON201.CreatedAt,
+				}
 			}
 			return nil
 		}
@@ -82,6 +110,17 @@ func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig con
 			return err
 		}
 	}
+	if len(toUpdate) > 1 {
+		var body []api.BulkUpdateFunctionBody
+		for _, b := range toUpdate {
+			body = append(body, b)
+		}
+		if resp, err := s.client.V1BulkUpdateFunctionsWithResponse(ctx, s.project, body); err != nil {
+			return errors.Errorf("failed to bulk update: %w", err)
+		} else if resp.JSON200 == nil {
+			return errors.Errorf("unexpected bulk update status %d: %s", resp.StatusCode(), string(resp.Body))
+		}
+	}
 	return nil
 }
 
diff --git a/internal/functions/deploy/upload.go b/pkg/function/deploy.go
similarity index 78%
rename from internal/functions/deploy/upload.go
rename to pkg/function/deploy.go
index 06accca5..3eef7928 100644
--- a/internal/functions/deploy/upload.go
+++ b/pkg/function/deploy.go
@@ -1,4 +1,4 @@
-package deploy
+package function
 
 import (
 	"bytes"
@@ -6,6 +6,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"io/fs"
 	"mime/multipart"
 	"os"
 	"path"
@@ -14,18 +15,19 @@ import (
 	"strings"
 
 	"github.com/go-errors/errors"
-	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/utils"
-	"github.com/supabase/cli/internal/utils/flags"
 	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/cast"
 	"github.com/supabase/cli/pkg/config"
 	"github.com/supabase/cli/pkg/queue"
+	"github.com/tidwall/jsonc"
 )
 
-var errNoDeploy = errors.New("All Functions are up to date.")
+var ErrNoDeploy = errors.New("All Functions are up to date.")
 
-func deploy(ctx context.Context, functionConfig config.FunctionConfig, maxJobs uint, fsys afero.Fs) error {
+func (s *EdgeRuntimeAPI) Deploy(ctx context.Context, functionConfig config.FunctionConfig, fsys fs.FS) error {
+	if s.eszip != nil {
+		return s.UpsertFunctions(ctx, functionConfig)
+	}
 	var toDeploy []api.FunctionDeployMetadata
 	for slug, fc := range functionConfig {
 		if !fc.Enabled {
@@ -46,17 +48,17 @@ func deploy(ctx context.Context, functionConfig config.FunctionConfig, maxJobs u
 		toDeploy = append(toDeploy, meta)
 	}
 	if len(toDeploy) == 0 {
-		return errors.New(errNoDeploy)
+		return errors.New(ErrNoDeploy)
 	} else if len(toDeploy) == 1 {
 		param := api.V1DeployAFunctionParams{Slug: toDeploy[0].Name}
-		_, err := upload(ctx, param, toDeploy[0], fsys)
+		_, err := s.upload(ctx, param, toDeploy[0], fsys)
 		return err
 	}
-	return bulkUpload(ctx, toDeploy, maxJobs, fsys)
+	return s.bulkUpload(ctx, toDeploy, fsys)
 }
 
-func bulkUpload(ctx context.Context, toDeploy []api.FunctionDeployMetadata, maxJobs uint, fsys afero.Fs) error {
-	jq := queue.NewJobQueue(maxJobs)
+func (s *EdgeRuntimeAPI) bulkUpload(ctx context.Context, toDeploy []api.FunctionDeployMetadata, fsys fs.FS) error {
+	jq := queue.NewJobQueue(s.maxJobs)
 	toUpdate := make([]api.BulkUpdateFunctionBody, len(toDeploy))
 	for i, meta := range toDeploy {
 		fmt.Fprintln(os.Stderr, "Deploying Function:", *meta.Name)
@@ -65,7 +67,7 @@ func bulkUpload(ctx context.Context, toDeploy []api.FunctionDeployMetadata, maxJ
 			BundleOnly: cast.Ptr(true),
 		}
 		bundle := func() error {
-			resp, err := upload(ctx, param, meta, fsys)
+			resp, err := s.upload(ctx, param, meta, fsys)
 			if err != nil {
 				return err
 			}
@@ -88,7 +90,7 @@ func bulkUpload(ctx context.Context, toDeploy []api.FunctionDeployMetadata, maxJ
 	if err := jq.Collect(); err != nil {
 		return err
 	}
-	if resp, err := utils.GetSupabase().V1BulkUpdateFunctionsWithResponse(ctx, flags.ProjectRef, toUpdate); err != nil {
+	if resp, err := s.client.V1BulkUpdateFunctionsWithResponse(ctx, s.project, toUpdate); err != nil {
 		return errors.Errorf("failed to bulk update: %w", err)
 	} else if resp.JSON200 == nil {
 		return errors.Errorf("unexpected bulk update status %d: %s", resp.StatusCode(), string(resp.Body))
@@ -96,7 +98,7 @@ func bulkUpload(ctx context.Context, toDeploy []api.FunctionDeployMetadata, maxJ
 	return nil
 }
 
-func upload(ctx context.Context, param api.V1DeployAFunctionParams, meta api.FunctionDeployMetadata, fsys afero.Fs) (*api.DeployFunctionResponse, error) {
+func (s *EdgeRuntimeAPI) upload(ctx context.Context, param api.V1DeployAFunctionParams, meta api.FunctionDeployMetadata, fsys fs.FS) (*api.DeployFunctionResponse, error) {
 	body, w := io.Pipe()
 	form := multipart.NewWriter(w)
 	ctx, cancel := context.WithCancelCause(ctx)
@@ -109,7 +111,7 @@ func upload(ctx context.Context, param api.V1DeployAFunctionParams, meta api.Fun
 			cancel(err)
 		}
 	}()
-	resp, err := utils.GetSupabase().V1DeployAFunctionWithBodyWithResponse(ctx, flags.ProjectRef, &param, form.FormDataContentType(), body)
+	resp, err := s.client.V1DeployAFunctionWithBodyWithResponse(ctx, s.project, &param, form.FormDataContentType(), body)
 	if cause := context.Cause(ctx); cause != ctx.Err() {
 		return nil, cause
 	} else if err != nil {
@@ -120,7 +122,7 @@ func upload(ctx context.Context, param api.V1DeployAFunctionParams, meta api.Fun
 	return resp.JSON201, nil
 }
 
-func writeForm(form *multipart.Writer, meta api.FunctionDeployMetadata, fsys afero.Fs) error {
+func writeForm(form *multipart.Writer, meta api.FunctionDeployMetadata, fsys fs.FS) error {
 	m, err := form.CreateFormField("metadata")
 	if err != nil {
 		return errors.Errorf("failed to create metadata: %w", err)
@@ -152,9 +154,9 @@ func writeForm(form *multipart.Writer, meta api.FunctionDeployMetadata, fsys afe
 		return nil
 	}
 	// Add import map
-	importMap := utils.ImportMap{}
+	importMap := ImportMap{}
 	if imPath := cast.Val(meta.ImportMapPath, ""); len(imPath) > 0 {
-		data, err := afero.ReadFile(fsys, filepath.FromSlash(imPath))
+		data, err := fs.ReadFile(fsys, filepath.FromSlash(imPath))
 		if err != nil {
 			return errors.Errorf("failed to load import map: %w", err)
 		}
@@ -173,9 +175,9 @@ func writeForm(form *multipart.Writer, meta api.FunctionDeployMetadata, fsys afe
 	}
 	// Add static files
 	patterns := config.Glob(cast.Val(meta.StaticPatterns, []string{}))
-	files, err := patterns.Files(afero.NewIOFS(fsys))
+	files, err := patterns.Files(fsys)
 	if err != nil {
-		fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), err)
+		fmt.Fprintln(os.Stderr, "WARN:", err)
 	}
 	for _, sfPath := range files {
 		if err := addFile(sfPath, io.Discard); err != nil {
@@ -185,10 +187,24 @@ func writeForm(form *multipart.Writer, meta api.FunctionDeployMetadata, fsys afe
 	return walkImportPaths(meta.EntrypointPath, importMap, addFile)
 }
 
+type ImportMap struct {
+	Imports map[string]string            `json:"imports"`
+	Scopes  map[string]map[string]string `json:"scopes"`
+}
+
+func (m *ImportMap) Parse(data []byte) error {
+	data = jsonc.ToJSONInPlace(data)
+	decoder := json.NewDecoder(bytes.NewReader(data))
+	if err := decoder.Decode(&m); err != nil {
+		return errors.Errorf("failed to parse import map: %w", err)
+	}
+	return nil
+}
+
 // Ref: https://regex101.com/r/DfBdJA/1
 var importPathPattern = regexp.MustCompile(`(?i)(?:import|export)\s+(?:{[^{}]+}|.*?)\s*(?:from)?\s*['"](.*?)['"]|import\(\s*['"](.*?)['"]\)`)
 
-func walkImportPaths(srcPath string, importMap utils.ImportMap, readFile func(curr string, w io.Writer) error) error {
+func walkImportPaths(srcPath string, importMap ImportMap, readFile func(curr string, w io.Writer) error) error {
 	seen := map[string]struct{}{}
 	// DFS because it's more efficient to pop from end of array
 	q := make([]string, 1)
@@ -204,7 +220,7 @@ func walkImportPaths(srcPath string, importMap utils.ImportMap, readFile func(cu
 		// Read into memory for regex match later
 		var buf bytes.Buffer
 		if err := readFile(curr, &buf); errors.Is(err, os.ErrNotExist) {
-			fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), err)
+			fmt.Fprintln(os.Stderr, "WARN:", err)
 			continue
 		} else if err != nil {
 			return err
diff --git a/internal/functions/deploy/testdata/geometries/Geometries.js b/pkg/function/testdata/geometries/Geometries.js
similarity index 100%
rename from internal/functions/deploy/testdata/geometries/Geometries.js
rename to pkg/function/testdata/geometries/Geometries.js
diff --git a/internal/functions/deploy/testdata/modules/imports.ts b/pkg/function/testdata/modules/imports.ts
similarity index 100%
rename from internal/functions/deploy/testdata/modules/imports.ts
rename to pkg/function/testdata/modules/imports.ts
diff --git a/internal/functions/deploy/testdata/nested/deno.json b/pkg/function/testdata/nested/deno.json
similarity index 100%
rename from internal/functions/deploy/testdata/nested/deno.json
rename to pkg/function/testdata/nested/deno.json
diff --git a/internal/functions/deploy/testdata/nested/index.ts b/pkg/function/testdata/nested/index.ts
similarity index 100%
rename from internal/functions/deploy/testdata/nested/index.ts
rename to pkg/function/testdata/nested/index.ts
diff --git a/internal/functions/deploy/testdata/shared/whatever.ts b/pkg/function/testdata/shared/whatever.ts
similarity index 100%
rename from internal/functions/deploy/testdata/shared/whatever.ts
rename to pkg/function/testdata/shared/whatever.ts
diff --git a/internal/functions/deploy/testdata/writes_import_map.form b/pkg/function/testdata/writes_import_map.form
similarity index 100%
rename from internal/functions/deploy/testdata/writes_import_map.form
rename to pkg/function/testdata/writes_import_map.form
diff --git a/pkg/migration/list.go b/pkg/migration/list.go
index ec8bf2ac..af76430d 100644
--- a/pkg/migration/list.go
+++ b/pkg/migration/list.go
@@ -39,6 +39,7 @@ func ListLocalMigrations(migrationsDir string, fsys fs.FS, filter ...func(string
 		filter = append(filter, func(string) bool { return true })
 	}
 	var clean []string
+OUTER:
 	for i, migration := range localMigrations {
 		filename := migration.Name()
 		if i == 0 && shouldSkip(filename) {
@@ -52,10 +53,11 @@ func ListLocalMigrations(migrationsDir string, fsys fs.FS, filter ...func(string
 		}
 		path := filepath.Join(migrationsDir, filename)
 		for _, keep := range filter {
-			if version := matches[1]; keep(version) {
-				clean = append(clean, path)
+			if version := matches[1]; !keep(version) {
+				continue OUTER
 			}
 		}
+		clean = append(clean, path)
 	}
 	return clean, nil
 }
diff --git a/pkg/migration/queries/drop.sql b/pkg/migration/queries/drop.sql
index 71564659..ed07c724 100644
--- a/pkg/migration/queries/drop.sql
+++ b/pkg/migration/queries/drop.sql
@@ -20,6 +20,28 @@ begin
     execute format('drop routine if exists %I.%I(%s) cascade', rec.pronamespace::regnamespace::name, rec.proname, pg_catalog.pg_get_function_identity_arguments(rec.oid));
   end loop;
 
+  -- views (necessary for views referencing objects in Supabase-managed schemas)
+  for rec in
+    select *
+    from pg_class c
+    where
+      c.relnamespace::regnamespace::name = 'public'
+      and c.relkind = 'v'
+  loop
+    execute format('drop view if exists %I.%I cascade', rec.relnamespace::regnamespace::name, rec.relname);
+  end loop;
+
+  -- materialized views (necessary for materialized views referencing objects in Supabase-managed schemas)
+  for rec in
+    select *
+    from pg_class c
+    where
+      c.relnamespace::regnamespace::name = 'public'
+      and c.relkind = 'm'
+  loop
+    execute format('drop materialized view if exists %I.%I cascade', rec.relnamespace::regnamespace::name, rec.relname);
+  end loop;
+
   -- tables (cascade to views)
   for rec in
     select *
diff --git a/pkg/storage/batch.go b/pkg/storage/batch.go
index 63638e9a..2e62774e 100644
--- a/pkg/storage/batch.go
+++ b/pkg/storage/batch.go
@@ -22,12 +22,13 @@ func (s *StorageAPI) UpsertBuckets(ctx context.Context, bucketConfig config.Buck
 	for _, b := range buckets {
 		exists[b.Name] = b.Id
 	}
+OUTER:
 	for name, bucket := range bucketConfig {
 		// Update bucket properties if already exists
 		if bucketId, ok := exists[name]; ok {
 			for _, keep := range filter {
 				if !keep(bucketId) {
-					continue
+					continue OUTER
 				}
 			}
 			fmt.Fprintln(os.Stderr, "Updating Storage bucket:", bucketId)


Test patch:
diff --git a/internal/db/branch/create/create_test.go b/internal/db/branch/create/create_test.go
index 8e384234..66b919ec 100644
--- a/internal/db/branch/create/create_test.go
+++ b/internal/db/branch/create/create_test.go
@@ -5,7 +5,7 @@ import (
 	"net/http"
 	"testing"
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -60,7 +60,7 @@ func TestBranchCreation(t *testing.T) {
 		// Run test
 		err := createBranch(context.Background(), "test-branch")
 		// Validate api
-		assert.ErrorContains(t, err, "request returned Service Unavailable for API route and version")
+		assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 
@@ -71,7 +71,7 @@ func TestBranchCreation(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/exec").
 			Reply(http.StatusCreated).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Run test
 		err := createBranch(context.Background(), "test-branch")
 		// Validate api
diff --git a/internal/db/branch/switch_/switch__test.go b/internal/db/branch/switch_/switch__test.go
index 4e2e1055..3eca45cd 100644
--- a/internal/db/branch/switch_/switch__test.go
+++ b/internal/db/branch/switch_/switch__test.go
@@ -6,7 +6,7 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
@@ -34,7 +34,7 @@ func TestSwitchCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		gock.New(utils.Docker.DaemonHost()).
 			Post("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusServiceUnavailable)
@@ -99,7 +99,7 @@ func TestSwitchCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Run test
 		err := Run(context.Background(), "postgres", fsys)
 		// Check error
@@ -117,7 +117,7 @@ func TestSwitchCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Run test
 		err := Run(context.Background(), "main", fsys)
 		// Check error
@@ -135,7 +135,7 @@ func TestSwitchCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Setup target branch
 		branch := "main"
 		branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch)
@@ -159,7 +159,7 @@ func TestSwitchCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Setup target branch
 		branch := "target"
 		branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch)
@@ -183,7 +183,7 @@ func TestSwitchCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Setup target branch
 		branch := "main"
 		branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch)
diff --git a/internal/db/diff/diff_test.go b/internal/db/diff/diff_test.go
index 20094d90..71b03223 100644
--- a/internal/db/diff/diff_test.go
+++ b/internal/db/diff/diff_test.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
@@ -51,10 +52,10 @@ func TestRun(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-shadow-realtime")
@@ -218,8 +219,8 @@ func TestDiffDatabase(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: false,
 					Status:  "exited",
 				},
@@ -248,10 +249,10 @@ func TestDiffDatabase(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
@@ -285,10 +286,10 @@ create schema public`)
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
diff --git a/internal/db/dump/dump_test.go b/internal/db/dump/dump_test.go
index 3a7c3cc6..e8e4b2d1 100644
--- a/internal/db/dump/dump_test.go
+++ b/internal/db/dump/dump_test.go
@@ -72,7 +72,7 @@ func TestPullCommand(t *testing.T) {
 		// Run test
 		err := Run(context.Background(), "", dbConfig, nil, nil, false, false, false, false, false, fsys)
 		// Check error
-		assert.ErrorContains(t, err, "request returned Service Unavailable for API route and version")
+		assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 
diff --git a/internal/db/lint/lint_test.go b/internal/db/lint/lint_test.go
index 8d7abef8..e8f4bc9a 100644
--- a/internal/db/lint/lint_test.go
+++ b/internal/db/lint/lint_test.go
@@ -7,7 +7,7 @@ import (
 	"net/http"
 	"testing"
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
@@ -38,7 +38,7 @@ func TestLintCommand(t *testing.T) {
 	gock.New(utils.Docker.DaemonHost()).
 		Get("/v" + utils.Docker.ClientVersion() + "/containers").
 		Reply(http.StatusOK).
-		JSON(types.ContainerJSON{})
+		JSON(container.InspectResponse{})
 	// Setup db response
 	expected := Result{
 		Function: "22751",
diff --git a/internal/db/reset/reset_test.go b/internal/db/reset/reset_test.go
index 4e3558be..bb87bd91 100644
--- a/internal/db/reset/reset_test.go
+++ b/internal/db/reset/reset_test.go
@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
@@ -50,7 +51,7 @@ func TestResetCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		gock.New(utils.Docker.DaemonHost()).
 			Delete("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
 			Reply(http.StatusOK)
@@ -61,10 +62,10 @@ func TestResetCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		// Setup mock postgres
@@ -84,10 +85,10 @@ func TestResetCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.StorageId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Config.Api.ExternalUrl).
@@ -147,7 +148,7 @@ func TestResetCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		gock.New(utils.Docker.DaemonHost()).
 			Delete("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
 			ReplyError(errors.New("network error"))
@@ -298,10 +299,10 @@ func TestRestartDatabase(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		// Restarts services
@@ -333,10 +334,10 @@ func TestRestartDatabase(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		// Restarts services
@@ -389,8 +390,8 @@ func TestRestartDatabase(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-reset/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: false,
 					Status:  "exited",
 				},
diff --git a/internal/db/start/start_test.go b/internal/db/start/start_test.go
index fdc76c6e..4cad4fb1 100644
--- a/internal/db/start/start_test.go
+++ b/internal/db/start/start_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/volume"
 	"github.com/h2non/gock"
 	"github.com/spf13/afero"
@@ -71,10 +72,10 @@ func TestStartDatabase(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-realtime")
@@ -117,10 +118,10 @@ func TestStartDatabase(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		// Run test
@@ -147,11 +148,11 @@ func TestStartDatabase(t *testing.T) {
 			ReplyError(errors.New("network error"))
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Config.Db.Image) + "/json").
-			Reply(http.StatusInternalServerError)
+			Reply(http.StatusServiceUnavailable)
 		// Run test
 		err := StartDatabase(context.Background(), "", fsys, io.Discard)
 		// Check error
-		assert.ErrorContains(t, err, "request returned Internal Server Error for API route and version")
+		assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 }
@@ -194,7 +195,7 @@ func TestStartCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Run test
 		err := Run(context.Background(), "", fsys)
 		// Check error
@@ -333,10 +334,10 @@ func TestStartDatabaseWithCustomSettings(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 
diff --git a/internal/functions/deploy/deploy_test.go b/internal/functions/deploy/deploy_test.go
index 9a5b9978..9510d4c7 100644
--- a/internal/functions/deploy/deploy_test.go
+++ b/internal/functions/deploy/deploy_test.go
@@ -54,6 +54,10 @@ func TestDeployCommand(t *testing.T) {
 			apitest.MockDockerStart(utils.Docker, imageUrl, containerId)
 			require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "bundled"))
 		}
+		gock.New(utils.DefaultApiHost).
+			Put("/v1/projects/" + flags.ProjectRef + "/functions").
+			Reply(http.StatusOK).
+			JSON(api.BulkUpdateFunctionResponse{})
 		// Setup output file
 		for _, v := range functions {
 			outputDir := filepath.Join(utils.TempDir, fmt.Sprintf(".output_%s", v))
diff --git a/internal/functions/new/new_test.go b/internal/functions/new/new_test.go
index 8e00fa65..df082d0d 100644
--- a/internal/functions/new/new_test.go
+++ b/internal/functions/new/new_test.go
@@ -25,6 +25,10 @@ func TestNewCommand(t *testing.T) {
 			"curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/test-func'",
 		)
 
+		// Verify config.toml is updated
+		_, err = afero.ReadFile(fsys, utils.ConfigPath)
+		assert.NoError(t, err, "config.toml should be created")
+
 		// Verify deno.json exists
 		denoPath := filepath.Join(utils.FunctionsDir, "test-func", "deno.json")
 		_, err = afero.ReadFile(fsys, denoPath)
diff --git a/internal/functions/serve/serve_test.go b/internal/functions/serve/serve_test.go
index 81c385bd..5f66d8e9 100644
--- a/internal/functions/serve/serve_test.go
+++ b/internal/functions/serve/serve_test.go
@@ -7,7 +7,7 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -30,7 +30,7 @@ func TestServeCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_test/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		containerId := "supabase_edge_runtime_test"
 		gock.New(utils.Docker.DaemonHost()).
 			Delete("/v" + utils.Docker.ClientVersion() + "/containers/" + containerId).
@@ -80,7 +80,7 @@ func TestServeCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_test/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Run test
 		err := Run(context.Background(), ".env", nil, "", RuntimeOption{}, fsys)
 		// Check error
@@ -100,7 +100,7 @@ func TestServeCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_test/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		// Run test
 		err := Run(context.Background(), ".env", cast.Ptr(true), "import_map.json", RuntimeOption{}, fsys)
 		// Check error
diff --git a/internal/gen/types/types_test.go b/internal/gen/types/types_test.go
index 811ae061..bf7ee406 100644
--- a/internal/gen/types/types_test.go
+++ b/internal/gen/types/types_test.go
@@ -6,7 +6,7 @@ import (
 	"net/http"
 	"testing"
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
@@ -41,7 +41,7 @@ func TestGenLocalCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		apitest.MockDockerStart(utils.Docker, imageUrl, containerId)
 		require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world\n"))
 		// Setup mock postgres
@@ -78,7 +78,7 @@ func TestGenLocalCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/images").
 			Reply(http.StatusServiceUnavailable)
@@ -99,7 +99,7 @@ func TestGenLocalCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId).
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 		apitest.MockDockerStart(utils.Docker, imageUrl, containerId)
 		require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world\n"))
 		// Setup mock postgres
diff --git a/internal/migration/squash/squash_test.go b/internal/migration/squash/squash_test.go
index b9092d98..bda678b0 100644
--- a/internal/migration/squash/squash_test.go
+++ b/internal/migration/squash/squash_test.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
@@ -59,10 +60,10 @@ func TestSquashCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
@@ -219,8 +220,8 @@ func TestSquashMigrations(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: false,
 					Status:  "exited",
 				},
@@ -249,10 +250,10 @@ func TestSquashMigrations(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
@@ -284,10 +285,10 @@ func TestSquashMigrations(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
diff --git a/internal/projects/apiKeys/api_keys_test.go b/internal/projects/apiKeys/api_keys_test.go
index 57130f15..48730a0a 100644
--- a/internal/projects/apiKeys/api_keys_test.go
+++ b/internal/projects/apiKeys/api_keys_test.go
@@ -3,6 +3,7 @@ package apiKeys
 import (
 	"context"
 	"errors"
+	"net/http"
 	"testing"
 
 	"github.com/h2non/gock"
@@ -14,14 +15,15 @@ import (
 )
 
 func TestProjectApiKeysCommand(t *testing.T) {
+	// Setup valid project ref
+	project := apitest.RandomProjectRef()
+	// Setup valid access token
+	token := apitest.RandomAccessToken(t)
+	t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
+
 	t.Run("lists all api-keys", func(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
-		// Setup valid project ref
-		project := apitest.RandomProjectRef()
-		// Setup valid access token
-		token := apitest.RandomAccessToken(t)
-		t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
 		// Flush pending mocks after test execution
 		defer gock.OffAll()
 		gock.New(utils.DefaultApiHost).
@@ -38,32 +40,32 @@ func TestProjectApiKeysCommand(t *testing.T) {
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 
-	t.Run("throws error on missing access token", func(t *testing.T) {
+	t.Run("throws error on network error", func(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
+		// Flush pending mocks after test execution
+		defer gock.OffAll()
+		gock.New(utils.DefaultApiHost).
+			Get("/v1/projects/" + project + "/api-keys").
+			ReplyError(errors.New("network error"))
 		// Run test
-		err := Run(context.Background(), "", fsys)
+		err := Run(context.Background(), project, fsys)
 		// Check error
-		assert.ErrorContains(t, err, "Unexpected error retrieving project api-keys")
+		assert.ErrorContains(t, err, "network error")
+		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 
-	t.Run("throws error on network error", func(t *testing.T) {
+	t.Run("throws error on service unavailable", func(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
-		// Setup valid project ref
-		project := apitest.RandomProjectRef()
-		// Setup valid access token
-		token := apitest.RandomAccessToken(t)
-		t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
 		// Flush pending mocks after test execution
 		defer gock.OffAll()
 		gock.New(utils.DefaultApiHost).
 			Get("/v1/projects/" + project + "/api-keys").
-			ReplyError(errors.New("network error"))
+			Reply(http.StatusServiceUnavailable)
 		// Run test
 		err := Run(context.Background(), project, fsys)
 		// Check error
-		assert.ErrorContains(t, err, "network error")
-		assert.Empty(t, apitest.ListUnmatchedRequests())
+		assert.ErrorContains(t, err, "unexpected get api keys status 503:")
 	})
 }
diff --git a/internal/start/start_test.go b/internal/start/start_test.go
index c00d2e92..d52bc459 100644
--- a/internal/start/start_test.go
+++ b/internal/start/start_test.go
@@ -9,6 +9,8 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/volume"
 	"github.com/h2non/gock"
@@ -52,9 +54,9 @@ func TestStartCommand(t *testing.T) {
 	})
 
 	t.Run("show status if database is already running", func(t *testing.T) {
-		var running []types.Container
+		var running []container.Summary
 		for _, name := range utils.GetDockerIds() {
-			running = append(running, types.Container{
+			running = append(running, container.Summary{
 				Names: []string{name + "_test"},
 			})
 		}
@@ -67,13 +69,15 @@ func TestStartCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
+			JSON(container.InspectResponse{})
 
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_start/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{Running: true},
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
+					Running: true,
+				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
@@ -103,13 +107,13 @@ func TestDatabaseStart(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/images/" + imageUrl + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ImageInspect{})
-		for _, image := range config.Images.Services() {
-			service := utils.GetRegistryImageUrl(image)
+			JSON(image.InspectResponse{})
+		for _, img := range config.Images.Services() {
+			service := utils.GetRegistryImageUrl(img)
 			gock.New(utils.Docker.DaemonHost()).
 				Get("/v" + utils.Docker.ClientVersion() + "/images/" + service + "/json").
 				Reply(http.StatusOK).
-				JSON(types.ImageInspect{})
+				JSON(image.InspectResponse{})
 		}
 		// Start postgres
 		utils.DbId = "test-postgres"
@@ -164,14 +168,14 @@ func TestDatabaseStart(t *testing.T) {
 			utils.StorageId, utils.ImgProxyId, utils.EdgeRuntimeId, utils.PgmetaId, utils.StudioId,
 			utils.LogflareId, utils.RestId, utils.VectorId,
 		}
-		for _, container := range started {
+		for _, c := range started {
 			gock.New(utils.Docker.DaemonHost()).
-				Get("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/json").
+				Get("/v" + utils.Docker.ClientVersion() + "/containers/" + c + "/json").
 				Reply(http.StatusOK).
-				JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-					State: &types.ContainerState{
+				JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+					State: &container.State{
 						Running: true,
-						Health:  &types.Health{Status: types.Healthy},
+						Health:  &container.Health{Status: types.Healthy},
 					},
 				}})
 		}
@@ -185,10 +189,10 @@ func TestDatabaseStart(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.StorageId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		gock.New(utils.Config.Api.ExternalUrl).
@@ -219,7 +223,7 @@ func TestDatabaseStart(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/images/" + imageUrl + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ImageInspect{})
+			JSON(image.InspectResponse{})
 		// Start postgres
 		utils.DbId = "test-postgres"
 		utils.ConfigId = "test-config"
@@ -233,10 +237,10 @@ func TestDatabaseStart(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
 					Running: true,
-					Health:  &types.Health{Status: types.Healthy},
+					Health:  &container.Health{Status: types.Healthy},
 				},
 			}})
 		// Run test
diff --git a/internal/status/status_test.go b/internal/status/status_test.go
index 01dd1e73..c7bfc1bc 100644
--- a/internal/status/status_test.go
+++ b/internal/status/status_test.go
@@ -7,7 +7,7 @@ import (
 	"net/http"
 	"testing"
 
-	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
 	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -18,9 +18,9 @@ import (
 
 func TestStatusCommand(t *testing.T) {
 	t.Run("shows service status", func(t *testing.T) {
-		var running []types.Container
+		var running []container.Summary
 		for _, name := range utils.GetDockerIds() {
-			running = append(running, types.Container{
+			running = append(running, container.Summary{
 				Names: []string{name + "_test"},
 			})
 		}
@@ -33,8 +33,10 @@ func TestStatusCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_test/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{
-				State: &types.ContainerState{Running: true},
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
+					Running: true,
+				},
 			}})
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
@@ -76,9 +78,9 @@ func TestStatusCommand(t *testing.T) {
 
 func TestServiceHealth(t *testing.T) {
 	t.Run("checks all services", func(t *testing.T) {
-		var running []types.Container
+		var running []container.Summary
 		for _, name := range utils.GetDockerIds() {
-			running = append(running, types.Container{
+			running = append(running, container.Summary{
 				Names: []string{"/" + name},
 			})
 		}
@@ -104,7 +106,7 @@ func TestServiceHealth(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
 			Reply(http.StatusOK).
-			JSON([]types.Container{})
+			JSON([]container.Summary{})
 		// Run test
 		stopped, err := checkServiceHealth(context.Background())
 		// Check error
diff --git a/internal/stop/stop_test.go b/internal/stop/stop_test.go
index 81fad5de..d260143d 100644
--- a/internal/stop/stop_test.go
+++ b/internal/stop/stop_test.go
@@ -8,7 +8,6 @@ import (
 	"net/http"
 	"testing"
 
-	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/volume"
@@ -32,7 +31,7 @@ func TestStopCommand(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
 			Reply(http.StatusOK).
-			JSON([]types.Container{})
+			JSON([]container.Summary{})
 		gock.New(utils.Docker.DaemonHost()).
 			Post("/v" + utils.Docker.ClientVersion() + "/containers/prune").
 			Reply(http.StatusOK).
@@ -69,7 +68,7 @@ func TestStopCommand(t *testing.T) {
 			Get("/v"+utils.Docker.ClientVersion()+"/containers/json").
 			MatchParam("all", "true").
 			Reply(http.StatusOK).
-			JSON([]types.Container{
+			JSON([]container.Summary{
 				{ID: "container1", Labels: map[string]string{utils.CliProjectLabel: "project1"}},
 				{ID: "container2", Labels: map[string]string{utils.CliProjectLabel: "project2"}},
 			})
@@ -93,7 +92,7 @@ func TestStopCommand(t *testing.T) {
 				MatchParam("all", "1").
 				MatchParam("filters", fmt.Sprintf(`{"label":{"com.supabase.cli.project=%s":true}}`, projectId)).
 				Reply(http.StatusOK).
-				JSON([]types.Container{{ID: "container-" + projectId, State: "running"}})
+				JSON([]container.Summary{{ID: "container-" + projectId, State: "running"}})
 
 			// Mock container stop
 			gock.New(utils.Docker.DaemonHost()).
@@ -120,11 +119,11 @@ func TestStopCommand(t *testing.T) {
 			Get("/v"+utils.Docker.ClientVersion()+"/containers/json").
 			MatchParam("all", "true").
 			Reply(http.StatusOK).
-			JSON([]types.Container{})
+			JSON([]container.Summary{})
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
 			Reply(http.StatusOK).
-			JSON([]types.Container{})
+			JSON([]container.Summary{})
 
 		// Run test
 		err := Run(context.Background(), true, "", true, fsys)
@@ -157,14 +156,14 @@ func TestStopCommand(t *testing.T) {
 		// Run test
 		err := Run(context.Background(), false, "test", false, afero.NewReadOnlyFs(fsys))
 		// Check error
-		assert.ErrorContains(t, err, "request returned Service Unavailable for API route and version")
+		assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 }
 
 func TestStopServices(t *testing.T) {
 	t.Run("stops all services", func(t *testing.T) {
-		containers := []types.Container{{ID: "c1", State: "running"}, {ID: "c2"}}
+		containers := []container.Summary{{ID: "c1", State: "running"}, {ID: "c2"}}
 		// Setup mock docker
 		require.NoError(t, apitest.MockDocker(utils.Docker))
 		defer gock.OffAll()
@@ -232,7 +231,7 @@ func TestStopServices(t *testing.T) {
 		gock.New(utils.Docker.DaemonHost()).
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/json").
 			Reply(http.StatusOK).
-			JSON([]types.Container{})
+			JSON([]container.Summary{})
 		gock.New(utils.Docker.DaemonHost()).
 			Post("/v" + utils.Docker.ClientVersion() + "/containers/prune").
 			ReplyError(errors.New("network error"))
diff --git a/internal/testing/apitest/docker.go b/internal/testing/apitest/docker.go
index 556756df..17a14355 100644
--- a/internal/testing/apitest/docker.go
+++ b/internal/testing/apitest/docker.go
@@ -6,8 +6,8 @@ import (
 	"net/http"
 
 	"github.com/docker/docker/api"
-	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/volume"
@@ -33,11 +33,11 @@ func MockDocker(docker *client.Client) error {
 }
 
 // Ref: internal/utils/docker.go::DockerStart
-func MockDockerStart(docker *client.Client, image, containerID string) {
+func MockDockerStart(docker *client.Client, imageID, containerID string) {
 	gock.New(docker.DaemonHost()).
-		Get("/v" + docker.ClientVersion() + "/images/" + image + "/json").
+		Get("/v" + docker.ClientVersion() + "/images/" + imageID + "/json").
 		Reply(http.StatusOK).
-		JSON(types.ImageInspect{})
+		JSON(image.InspectResponse{})
 	gock.New(docker.DaemonHost()).
 		Post("/v" + docker.ClientVersion() + "/networks/create").
 		Reply(http.StatusCreated).
@@ -61,7 +61,7 @@ func MockDockerStop(docker *client.Client) {
 	gock.New(docker.DaemonHost()).
 		Get("/v" + docker.ClientVersion() + "/containers/json").
 		Reply(http.StatusOK).
-		JSON([]types.Container{})
+		JSON([]container.Summary{})
 	gock.New(docker.DaemonHost()).
 		Post("/v" + docker.ClientVersion() + "/containers/prune").
 		Reply(http.StatusOK).
@@ -95,9 +95,10 @@ func setupDockerLogs(docker *client.Client, containerID, stdout string, exitCode
 	gock.New(docker.DaemonHost()).
 		Get("/v" + docker.ClientVersion() + "/containers/" + containerID + "/json").
 		Reply(http.StatusOK).
-		JSON(types.ContainerJSONBase{State: &types.ContainerState{
-			ExitCode: exitCode,
-		}})
+		JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+			State: &container.State{
+				ExitCode: exitCode,
+			}}})
 	gock.New(docker.DaemonHost()).
 		Delete("/v" + docker.ClientVersion() + "/containers/" + containerID).
 		Reply(http.StatusOK)
diff --git a/internal/utils/docker_test.go b/internal/utils/docker_test.go
index 6a0cdf03..06920455 100644
--- a/internal/utils/docker_test.go
+++ b/internal/utils/docker_test.go
@@ -7,8 +7,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/stdcopy"
@@ -52,7 +52,7 @@ func TestPullImage(t *testing.T) {
 		gock.New(Docker.DaemonHost()).
 			Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ImageInspect{})
+			JSON(image.InspectResponse{})
 		// Run test
 		assert.NoError(t, DockerPullImageIfNotCached(context.Background(), imageId))
 		// Validate api
@@ -130,7 +130,7 @@ func TestRunOnce(t *testing.T) {
 		gock.New(Docker.DaemonHost()).
 			Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ImageInspect{})
+			JSON(image.InspectResponse{})
 		gock.New(Docker.DaemonHost()).
 			Post("/v" + Docker.ClientVersion() + "/networks/create").
 			Reply(http.StatusCreated).
@@ -152,7 +152,7 @@ func TestRunOnce(t *testing.T) {
 		gock.New(Docker.DaemonHost()).
 			Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ImageInspect{})
+			JSON(image.InspectResponse{})
 		gock.New(Docker.DaemonHost()).
 			Post("/v" + Docker.ClientVersion() + "/networks/create").
 			Reply(http.StatusCreated).
@@ -236,7 +236,7 @@ func TestRunOnce(t *testing.T) {
 			Reply(http.StatusOK)
 		// Run test
 		_, err = DockerRunOnce(context.Background(), imageId, nil, nil)
-		assert.ErrorContains(t, err, "request returned Service Unavailable for API route and version")
+		assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version")
 		// Validate api
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
@@ -259,7 +259,10 @@ func TestRunOnce(t *testing.T) {
 		gock.New("http:///var/run/docker.sock").
 			Get("/v" + Docker.ClientVersion() + "/containers/" + containerId + "/json").
 			Reply(http.StatusOK).
-			JSON(types.ContainerJSONBase{State: &types.ContainerState{ExitCode: 1}})
+			JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
+				State: &container.State{
+					ExitCode: 1,
+				}}})
 		gock.New(Docker.DaemonHost()).
 			Delete("/v" + Docker.ClientVersion() + "/containers/" + containerId).
 			Reply(http.StatusOK)
@@ -293,7 +296,7 @@ func TestExecOnce(t *testing.T) {
 		gock.New(Docker.DaemonHost()).
 			Post("/v" + Docker.ClientVersion() + "/containers/" + containerId + "/exec").
 			Reply(http.StatusAccepted).
-			JSON(types.IDResponse{ID: "test-command"})
+			JSON(container.ExecCreateResponse{ID: "test-command"})
 		// Run test
 		_, err := DockerExecOnce(context.Background(), containerId, nil, nil)
 		assert.Error(t, err)
diff --git a/pkg/config/auth_test.go b/pkg/config/auth_test.go
index 176cb79b..3bf9dae1 100644
--- a/pkg/config/auth_test.go
+++ b/pkg/config/auth_test.go
@@ -14,7 +14,8 @@ import (
 
 func newWithDefaults() auth {
 	return auth{
-		EnableSignup: true,
+		EnableSignup:           true,
+		AdditionalRedirectUrls: []string{},
 		Email: email{
 			EnableConfirmations: true,
 		},
@@ -1114,3 +1115,70 @@ func TestExternalDiff(t *testing.T) {
 		assert.Empty(t, string(diff))
 	})
 }
+
+func TestRateLimitsDiff(t *testing.T) {
+	t.Run("local and remote rate limits match", func(t *testing.T) {
+		// Setup auth with rate limits
+		c := newWithDefaults()
+		c.RateLimit.AnonymousUsers = 20
+		c.RateLimit.TokenRefresh = 30
+		c.RateLimit.SignInSignUps = 40
+		c.RateLimit.TokenVerifications = 50
+		c.RateLimit.EmailSent = 25
+		c.RateLimit.SmsSent = 35
+		c.Email.Smtp = &smtp{Enabled: true}
+		// Run test
+		diff, err := c.DiffWithRemote(v1API.AuthConfigResponse{
+			RateLimitAnonymousUsers: cast.Ptr(20),
+			RateLimitTokenRefresh:   cast.Ptr(30),
+			RateLimitOtp:            cast.Ptr(40),
+			RateLimitVerify:         cast.Ptr(50),
+			RateLimitEmailSent:      cast.Ptr(25),
+			RateLimitSmsSent:        cast.Ptr(35),
+			SmtpHost:                cast.Ptr(""),
+		})
+		// Check error
+		assert.NoError(t, err)
+		assert.Empty(t, string(diff))
+	})
+
+	t.Run("local and remote rate limits differ", func(t *testing.T) {
+		// Setup auth with rate limits
+		c := newWithDefaults()
+		c.RateLimit.AnonymousUsers = 20
+		c.RateLimit.TokenRefresh = 30
+		c.RateLimit.SignInSignUps = 40
+		c.RateLimit.TokenVerifications = 50
+		c.RateLimit.EmailSent = 25
+		c.RateLimit.SmsSent = 35
+		c.Email.Smtp = &smtp{Enabled: true}
+		// Run test with different remote values
+		diff, err := c.DiffWithRemote(v1API.AuthConfigResponse{
+			RateLimitAnonymousUsers: cast.Ptr(10), // Different value
+			RateLimitTokenRefresh:   cast.Ptr(30),
+			RateLimitOtp:            cast.Ptr(45), // Different value
+			RateLimitVerify:         cast.Ptr(50),
+			RateLimitEmailSent:      cast.Ptr(15), // Different value
+			RateLimitSmsSent:        cast.Ptr(55), // Different value
+			SmtpHost:                cast.Ptr(""),
+		})
+		// Check error
+		assert.NoError(t, err)
+		// Compare with snapshot
+		assertSnapshotEqual(t, diff)
+	})
+
+	t.Run("ignores email rate limit when smtp is disabled", func(t *testing.T) {
+		// Setup auth without rate limits
+		c := newWithDefaults()
+		c.RateLimit.EmailSent = 25
+		// Run test with remote rate limits
+		diff, err := c.DiffWithRemote(v1API.AuthConfigResponse{
+			RateLimitEmailSent: cast.Ptr(15),
+			SmtpHost:           cast.Ptr(""),
+		})
+		// Check error
+		assert.NoError(t, err)
+		assert.Empty(t, string(diff))
+	})
+}
diff --git a/pkg/config/secret_test.go b/pkg/config/secret_test.go
index c4bf74e5..a83d0766 100644
--- a/pkg/config/secret_test.go
+++ b/pkg/config/secret_test.go
@@ -22,7 +22,7 @@ func TestDecryptSecret(t *testing.T) {
 		// Run test
 		plaintext, err := decrypt("", value)
 		// Check error
-		assert.ErrorContains(t, err, "missing private key")
+		assert.ErrorIs(t, err, errMissingKey)
 		assert.Equal(t, value, plaintext)
 	})
 
diff --git a/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff b/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff
index b4022569..b997bb15 100644
--- a/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff
+++ b/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff
@@ -24,5 +24,5 @@ diff remote[auth] local[auth]
 +minimum_password_length = 6
 +password_requirements = "lower_upper_letters_digits_symbols"
  
- [hook]
- 
+ [rate_limit]
+ anonymous_users = 0
diff --git a/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff b/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff
index 84085ccd..514251aa 100644
--- a/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff
+++ b/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff
@@ -1,8 +1,8 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -10,7 +10,7 @@
- password_requirements = ""
+@@ -19,7 +19,7 @@
+ sms_sent = 0
  
  [captcha]
 -enabled = true
diff --git a/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff b/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff
index 43bb8802..7bbf9ee8 100644
--- a/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff
+++ b/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff
@@ -1,8 +1,8 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -10,9 +10,9 @@
- password_requirements = ""
+@@ -19,9 +19,9 @@
+ sms_sent = 0
  
  [captcha]
 -enabled = false
diff --git a/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff b/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff
index 99a50071..031c2c5f 100644
--- a/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff
+++ b/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -31,13 +31,13 @@
+@@ -40,13 +40,13 @@
  inactivity_timeout = "0s"
  
  [email]
diff --git a/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff b/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff
index fb42aaab..b9f20574 100644
--- a/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff
+++ b/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -31,36 +31,44 @@
+@@ -40,36 +40,44 @@
  inactivity_timeout = "0s"
  
  [email]
diff --git a/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff b/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff
index 8b78fb24..2eef288c 100644
--- a/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff
+++ b/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -71,7 +71,7 @@
+@@ -80,7 +80,7 @@
  
  [external]
  [external.apple]
@@ -10,7 +10,7 @@ diff remote[auth] local[auth]
  client_id = "test-client-1,test-client-2"
  secret = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"
  url = ""
-@@ -78,9 +78,9 @@
+@@ -87,9 +87,9 @@
  redirect_uri = ""
  skip_nonce_check = false
  [external.azure]
@@ -23,7 +23,7 @@ diff remote[auth] local[auth]
  url = ""
  redirect_uri = ""
  skip_nonce_check = false
-@@ -127,7 +127,7 @@
+@@ -136,7 +136,7 @@
  redirect_uri = ""
  skip_nonce_check = false
  [external.google]
diff --git a/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff b/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff
index 247618f0..b3cb9e1b 100644
--- a/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff
+++ b/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -11,19 +11,19 @@
+@@ -20,19 +20,19 @@
  
  [hook]
  [hook.mfa_verification_attempt]
diff --git a/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff b/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff
index 556b13ff..b618fb66 100644
--- a/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff
+++ b/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -11,20 +11,20 @@
+@@ -20,20 +20,20 @@
  
  [hook]
  [hook.mfa_verification_attempt]
diff --git a/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff b/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff
index e5fa7c47..dbb25a84 100644
--- a/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff
+++ b/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -12,16 +12,16 @@
+@@ -21,16 +21,16 @@
  [hook]
  
  [mfa]
diff --git a/pkg/config/testdata/TestRateLimitsDiff/local_and_remote_rate_limits_differ.diff b/pkg/config/testdata/TestRateLimitsDiff/local_and_remote_rate_limits_differ.diff
new file mode 100644
index 00000000..c403f9cb
--- /dev/null
+++ b/pkg/config/testdata/TestRateLimitsDiff/local_and_remote_rate_limits_differ.diff
@@ -0,0 +1,20 @@
+diff remote[auth] local[auth]
+--- remote[auth]
++++ local[auth]
+@@ -11,12 +11,12 @@
+ password_requirements = ""
+ 
+ [rate_limit]
+-anonymous_users = 10
++anonymous_users = 20
+ token_refresh = 30
+-sign_in_sign_ups = 45
++sign_in_sign_ups = 40
+ token_verifications = 50
+-email_sent = 15
+-sms_sent = 55
++email_sent = 25
++sms_sent = 35
+ 
+ [hook]
+ 
diff --git a/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff b/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff
index a7c312c9..a9b5eb63 100644
--- a/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff
+++ b/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -40,7 +40,7 @@
+@@ -49,7 +49,7 @@
  otp_expiry = 0
  
  [sms]
diff --git a/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff b/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff
index 1dd938cd..b1cf6e99 100644
--- a/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff
+++ b/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -40,12 +40,12 @@
+@@ -49,12 +49,12 @@
  otp_expiry = 0
  
  [sms]
@@ -19,7 +19,7 @@ diff remote[auth] local[auth]
  account_sid = ""
  message_service_sid = ""
  auth_token = ""
-@@ -68,8 +68,6 @@
+@@ -77,8 +77,6 @@
  api_key = ""
  api_secret = ""
  [sms.test_otp]
diff --git a/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff b/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff
index 02f0b76c..e2853e76 100644
--- a/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff
+++ b/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff
@@ -1,7 +1,7 @@
 diff remote[auth] local[auth]
 --- remote[auth]
 +++ local[auth]
-@@ -40,12 +40,12 @@
+@@ -49,12 +49,12 @@
  otp_expiry = 0
  
  [sms]
@@ -19,7 +19,7 @@ diff remote[auth] local[auth]
  account_sid = ""
  message_service_sid = ""
  auth_token = ""
-@@ -55,9 +55,9 @@
+@@ -64,9 +64,9 @@
  message_service_sid = ""
  auth_token = ""
  [sms.messagebird]
@@ -32,7 +32,7 @@ diff remote[auth] local[auth]
  [sms.textlocal]
  enabled = false
  sender = ""
-@@ -68,6 +68,7 @@
+@@ -77,6 +77,7 @@
  api_key = ""
  api_secret = ""
  [sms.test_otp]
diff --git a/pkg/config/testdata/config.toml b/pkg/config/testdata/config.toml
index c83baf08..bae13a5b 100644
--- a/pkg/config/testdata/config.toml
+++ b/pkg/config/testdata/config.toml
@@ -127,6 +127,20 @@ minimum_password_length = 6
 # are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols`
 password_requirements = ""
 
+[auth.rate_limit]
+# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled.
+email_sent = 2
+# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled.
+sms_sent = 30
+# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true.
+anonymous_users = 30
+# Number of sessions that can be refreshed in a 5 minute interval per IP address.
+token_refresh = 150
+# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users).
+sign_in_sign_ups = 30
+# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address.
+token_verifications = 30
+
 # Configure one of the supported captcha providers: `hcaptcha`, `turnstile`.
 [auth.captcha]
 enabled = true
@@ -246,6 +260,9 @@ enabled = true
 policy = "per_worker"
 inspector_port = 8083
 
+[edge_runtime.secrets]
+test_key = "test_value"
+
 [analytics]
 enabled = true
 port = 54327
diff --git a/pkg/config/updater_test.go b/pkg/config/updater_test.go
index f3eb0bcb..05a5d91d 100644
--- a/pkg/config/updater_test.go
+++ b/pkg/config/updater_test.go
@@ -190,10 +190,11 @@ func TestUpdateAuthConfig(t *testing.T) {
 			JSON(v1API.AuthConfigResponse{})
 		// Run test
 		err := updater.UpdateAuthConfig(context.Background(), "test-project", auth{
-			Enabled:      true,
-			EnableSignup: true,
-			Email:        email{EnableConfirmations: true},
-			Sms:          sms{TestOTP: map[string]string{}},
+			Enabled:                true,
+			EnableSignup:           true,
+			AdditionalRedirectUrls: []string{},
+			Email:                  email{EnableConfirmations: true},
+			Sms:                    sms{TestOTP: map[string]string{}},
 		})
 		// Check result
 		assert.NoError(t, err)
diff --git a/pkg/function/batch_test.go b/pkg/function/batch_test.go
index 101cabb7..f356e74d 100644
--- a/pkg/function/batch_test.go
+++ b/pkg/function/batch_test.go
@@ -29,7 +29,9 @@ const (
 func TestUpsertFunctions(t *testing.T) {
 	apiClient, err := api.NewClientWithResponses(mockApiHost)
 	require.NoError(t, err)
-	client := NewEdgeRuntimeAPI(mockProject, *apiClient, &MockBundler{})
+	client := NewEdgeRuntimeAPI(mockProject, *apiClient, func(era *EdgeRuntimeAPI) {
+		era.eszip = &MockBundler{}
+	})
 
 	t.Run("throws error on network failure", func(t *testing.T) {
 		// Setup mock api
diff --git a/internal/functions/deploy/upload_test.go b/pkg/function/deploy_test.go
similarity index 78%
rename from internal/functions/deploy/upload_test.go
rename to pkg/function/deploy_test.go
index 12c00c12..424478aa 100644
--- a/internal/functions/deploy/upload_test.go
+++ b/pkg/function/deploy_test.go
@@ -1,4 +1,4 @@
-package deploy
+package function
 
 import (
 	"bytes"
@@ -11,15 +11,12 @@ import (
 	"os"
 	"path"
 	"testing"
+	fs "testing/fstest"
 
 	"github.com/h2non/gock"
-	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
-	"github.com/supabase/cli/internal/testing/apitest"
-	"github.com/supabase/cli/internal/utils"
-	"github.com/supabase/cli/internal/utils/flags"
 	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/cast"
 	"github.com/supabase/cli/pkg/config"
@@ -52,7 +49,7 @@ func TestImportPaths(t *testing.T) {
 		fsys.On("ReadFile", "testdata/modules/imports.ts").Once()
 		fsys.On("ReadFile", "testdata/geometries/Geometries.js").Once()
 		// Run test
-		im := utils.ImportMap{}
+		im := ImportMap{}
 		err := walkImportPaths("testdata/modules/imports.ts", im, fsys.ReadFile)
 		// Check error
 		assert.NoError(t, err)
@@ -69,7 +66,7 @@ func TestImportPaths(t *testing.T) {
 		fsys.On("ReadFile", "testdata/shared/mod.ts").Once()
 		fsys.On("ReadFile", "testdata/nested/index.ts").Once()
 		// Run test
-		im := utils.ImportMap{Imports: map[string]string{
+		im := ImportMap{Imports: map[string]string{
 			"module-name/": "../shared/",
 		}}
 		err := walkImportPaths("testdata/modules/imports.ts", im, fsys.ReadFile)
@@ -94,7 +91,7 @@ func TestWriteForm(t *testing.T) {
 		form := multipart.NewWriter(&buf)
 		require.NoError(t, form.SetBoundary("test"))
 		// Setup in-memory fs
-		fsys := afero.FromIOFS{FS: testImports}
+		fsys := testImports
 		// Run test
 		err := writeForm(form, api.FunctionDeployMetadata{
 			Name:           cast.Ptr("nested"),
@@ -113,7 +110,7 @@ func TestWriteForm(t *testing.T) {
 		form := multipart.NewWriter(&buf)
 		require.NoError(t, form.SetBoundary("test"))
 		// Setup in-memory fs
-		fsys := afero.NewMemMapFs()
+		fsys := fs.MapFS{}
 		// Run test
 		err := writeForm(form, api.FunctionDeployMetadata{
 			ImportMapPath: cast.Ptr("testdata/import_map.json"),
@@ -127,7 +124,7 @@ func TestWriteForm(t *testing.T) {
 		form := multipart.NewWriter(&buf)
 		require.NoError(t, form.SetBoundary("test"))
 		// Setup in-memory fs
-		fsys := afero.NewMemMapFs()
+		fsys := testImports
 		// Run test
 		err := writeForm(form, api.FunctionDeployMetadata{
 			StaticPatterns: cast.Ptr([]string{"testdata"}),
@@ -138,10 +135,9 @@ func TestWriteForm(t *testing.T) {
 }
 
 func TestDeployAll(t *testing.T) {
-	flags.ProjectRef = apitest.RandomProjectRef()
-	// Setup valid access token
-	token := apitest.RandomAccessToken(t)
-	t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
+	apiClient, err := api.NewClientWithResponses(mockApiHost)
+	require.NoError(t, err)
+	client := NewEdgeRuntimeAPI(mockProject, *apiClient)
 
 	t.Run("deploys single slug", func(t *testing.T) {
 		c := config.FunctionConfig{"demo": {
@@ -149,19 +145,20 @@ func TestDeployAll(t *testing.T) {
 			Entrypoint: "testdata/shared/whatever.ts",
 		}}
 		// Setup in-memory fs
-		fsys := afero.FromIOFS{FS: testImports}
+		fsys := testImports
 		// Setup mock api
 		defer gock.OffAll()
-		gock.New(utils.DefaultApiHost).
-			Post("/v1/projects/"+flags.ProjectRef+"/functions/deploy").
+		gock.New(mockApiHost).
+			Post("/v1/projects/"+mockProject+"/functions/deploy").
 			MatchParam("slug", "demo").
 			Reply(http.StatusCreated).
 			JSON(api.DeployFunctionResponse{})
 		// Run test
-		err := deploy(context.Background(), c, 1, fsys)
+		err := client.Deploy(context.Background(), c, fsys)
 		// Check error
 		assert.NoError(t, err)
-		assert.Empty(t, apitest.ListUnmatchedRequests())
+		assert.Empty(t, gock.Pending())
+		assert.Empty(t, gock.GetUnmatchedRequests())
 	})
 
 	t.Run("deploys multiple slugs", func(t *testing.T) {
@@ -176,42 +173,44 @@ func TestDeployAll(t *testing.T) {
 			},
 		}
 		// Setup in-memory fs
-		fsys := afero.FromIOFS{FS: testImports}
+		fsys := testImports
 		// Setup mock api
 		defer gock.OffAll()
 		for slug := range c {
-			gock.New(utils.DefaultApiHost).
-				Post("/v1/projects/"+flags.ProjectRef+"/functions/deploy").
+			gock.New(mockApiHost).
+				Post("/v1/projects/"+mockProject+"/functions/deploy").
 				MatchParam("slug", slug).
 				Reply(http.StatusCreated).
 				JSON(api.DeployFunctionResponse{Id: slug})
 		}
-		gock.New(utils.DefaultApiHost).
-			Put("/v1/projects/" + flags.ProjectRef + "/functions").
+		gock.New(mockApiHost).
+			Put("/v1/projects/" + mockProject + "/functions").
 			Reply(http.StatusOK).
 			JSON(api.BulkUpdateFunctionResponse{})
 		// Run test
-		err := deploy(context.Background(), c, 1, fsys)
+		err := client.Deploy(context.Background(), c, fsys)
 		// Check error
 		assert.NoError(t, err)
-		assert.Empty(t, apitest.ListUnmatchedRequests())
+		assert.Empty(t, gock.Pending())
+		assert.Empty(t, gock.GetUnmatchedRequests())
 	})
 
 	t.Run("throws error on network failure", func(t *testing.T) {
 		errNetwork := errors.New("network")
 		c := config.FunctionConfig{"demo": {Enabled: true}}
 		// Setup in-memory fs
-		fsys := afero.NewMemMapFs()
+		fsys := fs.MapFS{}
 		// Setup mock api
 		defer gock.OffAll()
-		gock.New(utils.DefaultApiHost).
-			Post("/v1/projects/"+flags.ProjectRef+"/functions/deploy").
+		gock.New(mockApiHost).
+			Post("/v1/projects/"+mockProject+"/functions/deploy").
 			MatchParam("slug", "demo").
 			ReplyError(errNetwork)
 		// Run test
-		err := deploy(context.Background(), c, 1, fsys)
+		err := client.Deploy(context.Background(), c, fsys)
 		// Check error
 		assert.ErrorIs(t, err, errNetwork)
-		assert.Empty(t, apitest.ListUnmatchedRequests())
+		assert.Empty(t, gock.Pending())
+		assert.Empty(t, gock.GetUnmatchedRequests())
 	})
 }