Skip to content

Embedding VersityGW

Ben McClelland edited this page Sep 18, 2026 · 1 revision

The github.com/versity/versitygw/embedgw package runs VersityGW inside another Go application. It assembles the same S3, admin, WebUI, website, logging, metrics, event, and IAM services used by the command-line binary, while letting the host application own configuration and process signals.

Use embedgw.RunVersityGW for an S3 gateway and embedgw.RunIAMAPI for the standalone IAM/STS service. Both functions block until their context is canceled or a listener returns.

Basic lifecycle

An embedding application must:

  1. Construct a backend.Backend.
  2. Populate embedgw.Config, including required credentials, listener limits, multipart limit, and at least one S3 address.
  3. Create a cancelable context and install its own termination signal handling.
  4. Call RunVersityGW and treat a non-nil return as a startup or serving error.
package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/versity/versitygw/backend"
	"github.com/versity/versitygw/embedgw"
)

func main() {
	ctx, stop := signal.NotifyContext(
		context.Background(),
		os.Interrupt,
		syscall.SIGTERM,
	)
	defer stop()

	be := backend.New()
	err := embedgw.RunVersityGW(ctx, be, &embedgw.Config{
		RootUserAccess:   "root-access",
		RootUserSecret:   "root-secret",
		Ports:            []string{"127.0.0.1:7070"},
		MaxConnections:   100,
		MaxRequests:      100,
		MultipartMaxParts: 10000,
		KeepAlive:        true,
	})
	if err != nil {
		log.Fatal(err)
	}
}

This example compiles, but backend.New() returns BackendUnsupported and is not usable object storage. Replace it with a built-in backend constructor or an implementation of backend.Backend.

RunVersityGW does not install OS signal handlers. Canceling the context starts coordinated shutdown; a clean cancellation normally returns nil, not context.Canceled.

Warning

Pass non-nil backend and configuration values. RunVersityGW currently dereferences both before reporting a validation error.

Required configuration

RunVersityGW requires:

  • RootUserAccess and RootUserSecret.
  • One or more entries in Ports.
  • Positive MaxConnections, MaxRequests, and MultipartMaxParts.

An empty Region is changed to us-east-1. MaxRequests should not exceed MaxConnections; the gateway warns but does not reject that configuration.

Ports, AdminPorts, WebuiPorts, and WebsitePorts accept TCP addresses, file-backed UNIX sockets, and Linux abstract sockets. Listener groups cannot share an address. The SocketPerm field controls file-backed socket modes with an octal string such as 0660.

Only one RunVersityGW call may be active in a process. Only one RunIAMAPI call may also be active. Avoid running multiple embedded gateways in one process: debug configuration and bucket-name validation include process-global state.

Listener ownership

Configuration Behavior
Ports Required S3 API listener group
Empty AdminPorts Admin routes share the S3 listeners and S3 TLS
Non-empty AdminPorts Independent admin listener, limits, options, and optional TLS
WebuiPorts Independent WebUI listener
WebuiS3Prefix Mounts the WebUI on the S3 listener before S3 routes
WebsitePorts Independent static website listener using the same backend

A dedicated admin listener does not inherit the S3 certificate; without AdminCertFile and AdminKeyFile it is plaintext. Dedicated WebUI and website listeners inherit the S3 certificate when their own certificate pairs are omitted, unless WebuiNoTLS or WebsiteNoTLS disables TLS.

Mounting the WebUI with WebuiS3Prefix masks an S3 bucket whose name equals that prefix segment. The same consideration applies to a configured health path.

Each logical server owns all addresses in its list. If any listener group returns, the gateway shuts down every group and returns that listener's error.

Backend extension point

The caller supplies the storage implementation:

type Backend interface {
	fmt.Stringer
	Shutdown()
	NormalizeObjectKey(bucket, object string) string
	// Bucket, object, multipart, tagging, lock, website, and admin methods...
}

The complete interface is in backend/backend.go. Embed backend.BackendUnsupported when implementing only a subset of operations, and see Adding a Backend for behavioral requirements.

Once serving begins, RunVersityGW owns lifecycle shutdown for the supplied backend and calls Backend.Shutdown before returning. Do not close it while the gateway is active. Validation and construction can fail before the central serving cleanup is reached, so a caller that created external resources should clean them up after an early startup error.

S3 routes and middleware

Config.S3Options accepts s3api.Option values appended after the gateway's core options. Common extension helpers are:

s3api.WithRoute(fiber.MethodGet, "/extension/status", handler)
s3api.WithMiddleware("/extension", middleware)

These routes and middleware run after the concurrency limiter and before the S3 route table. A route is terminal unless it calls ctx.Next(). Middleware must call ctx.Next() for requests it does not handle, or it will mask S3 operations below its prefix.

Because options mutate the S3 server, later caller-supplied options can override internal option state. Keep extensions narrowly scoped and avoid reconfiguring gateway-owned routing, authentication, TLS, or limits.

AdminOptions provides the corresponding extension point for a dedicated admin server and has no effect when admin routes share the S3 listener.

Operational service injection

OnServicesReady runs synchronously after access logging, metrics, and event delivery are constructed but before the S3 server is created:

cfg.OnServicesReady = func(ops embedgw.OpsServices) {
	custom.Logger = ops.Logger
	custom.Metrics = ops.Metrics
	custom.Events = ops.Events
}

This is useful when a custom route or middleware was constructed before the gateway but needs the gateway's operational services. The callback has no error return and does not mean listeners have bound successfully.

IAM injection

Set Config.IAMService to supply an auth.IAMService directly. It takes precedence over every declarative IAM field and is passed to the S3 and admin servers unchanged:

cfg.IAMService = customIAM

When IAMService is nil, RunVersityGW calls auth.New(cfg.IamOpts()). Declarative IAM triggers use this precedence:

  1. StandaloneIAMEndpoint
  2. IAMDir
  3. LDAPServerURL
  4. S3IAMEndpoint
  5. VaultEndpointURL
  6. IpaHost

With none configured, the gateway uses root-only single-account mode.

Config.IamOpts() exposes the exact conversion to auth.Opts. Use it when an embedder must construct the same IAM service before gateway startup, such as when a custom route needs to authenticate through it.

The gateway calls Shutdown on an injected IAM service during coordinated shutdown. Do not reuse or independently stop it while RunVersityGW is active.

Certificate and log reload

Config.SigHup is an optional receive-only channel. The host application must translate SIGHUP or another reload trigger into a channel send:

hup := make(chan os.Signal, 1)
signal.Notify(hup, syscall.SIGHUP)
defer signal.Stop(hup)

reload := make(chan struct{}, 1)
go func() {
	for range hup {
		select {
		case reload <- struct{}{}:
		default:
		}
	}
}()
cfg.SigHup = reload

A reload rotates S3 and admin access-log files and reloads S3, dedicated admin, WebUI, and website certificates from their original paths. A logger reload error stops the gateway. A certificate reload error is logged while the server continues using the previous certificate.

Shutdown behavior

After context cancellation or a server return, shutdown proceeds through:

  1. S3, dedicated admin, WebUI, and website servers.
  2. Backend and IAM service.
  3. Access/admin loggers and event sender.
  4. Metrics manager and its background context.

Shutdown errors are written to stderr and do not replace the error that caused serving to stop. A service returning nil still initiates shutdown of the others.

Embedding the standalone IAM API

RunIAMAPI uses a separate embedgw.IAMConfig:

err := embedgw.RunIAMAPI(ctx, &embedgw.IAMConfig{
	RootUserAccess: "root-access",
	RootUserSecret: "root-secret",
	Ports:          []string{"127.0.0.1:7075"},
	MaxConnections: 100,
	MaxRequests:    100,
	IAMDir:         "/var/lib/versitygw/iam",
})

It can additionally run private IAM endpoints and a WebUI. Private TCP listeners require a server certificate, private key, and client CA together and enforce client certificates. Leaving all three empty is permitted only when every private listener is a UNIX socket.

The public, private, and WebUI services run concurrently. Context cancellation or the first server return shuts down all of them. IAMConfig.SigHup reloads public and private API certificates, but currently does not reload the IAM-hosted WebUI certificate.

Related documentation

Clone this wiki locally