Documentation
¶
Overview ¶
Package studio runs Tyk AI Studio inside another Go process. A host builds a Studio with New, serves its HTTP handler, optionally starts the embedded AI gateway and the gRPC control server, and calls Stop on shutdown. The standalone binary is a thin wrapper over the same package.
Only one Studio may run in a process at a time: several packages keep process-wide state (the configuration, the analytics recorder, the secrets key). New refuses a second instance until the first is stopped.
Index ¶
- Constants
- Variables
- func CheckSchema(ctx context.Context, db *gorm.DB) error
- func OpenDatabase(conf *config.AppConf) (*gorm.DB, error)
- func RegisterDatabaseDriver(name string, open func(dsn string) gorm.Dialector)
- type Authenticator
- type ControlPlane
- type ControlPlaneOptions
- type HostTykConnection
- type Identity
- type LicenseStatus
- type Options
- type Studio
- func (s *Studio) HTTPHandler() http.Handler
- func (s *Studio) LicenseStatus() LicenseStatus
- func (s *Studio) ListenAndServe(addr, certFile, keyFile string) error
- func (s *Studio) OAuthMetadataHandler() http.Handler
- func (s *Studio) ProxyHandler() http.Handler
- func (s *Studio) ReloadLicense() error
- func (s *Studio) StartGRPC(listener net.Listener) error
- func (s *Studio) StartProxy() error
- func (s *Studio) Stop(ctx context.Context) error
Constants ¶
const ( DefaultNodeLabel = "studio" DefaultControlPlaneLabel = "control-plane" )
Labels a replica gets when its host names none.
Variables ¶
var ( // ErrSchemaMissing: no Studio schema version is recorded. No Studio // has migrated the database, or the one that did predates schema // versions. ErrSchemaMissing = models.ErrSchemaMissing // ErrSchemaTooOld: the database was migrated by an older Studio than // this build needs; upgrade the Studio that migrates it first. ErrSchemaTooOld = models.ErrSchemaTooOld // ErrSchemaTooNew: a newer Studio migrated the database in a way this // build cannot read; upgrade this build. ErrSchemaTooNew = models.ErrSchemaTooNew )
The errors CheckSchema wraps.
var ErrAlreadyRunning = errors.New("studio: another instance is already running in this process")
ErrAlreadyRunning is returned by New while another Studio is running in the process.
var ErrControlPlaneNeedsPostgres = errors.New("studio: a headless control plane needs a Postgres database shared with a full Studio")
ErrControlPlaneNeedsPostgres is returned by NewControlPlane for a database other than Postgres: a headless control plane shares a full Studio's database, and replicas coordinate only through Postgres.
var ErrGatewayNotLicensed = errors.New("studio: feature_gateway is not in the licence entitlements")
ErrGatewayNotLicensed is returned by StartProxy when the licence does not include the gateway.
var ErrNotControlPlane = errors.New(`studio: the gRPC control server runs only when GatewayMode is "control"`)
ErrNotControlPlane is returned by StartGRPC unless Config.GatewayMode is "control".
Functions ¶
func CheckSchema ¶
CheckSchema reports whether this build of Studio can use db, as a full Studio (New) left it, without migrating it. It only reads: it creates and alters nothing, so it is safe against a database another process owns. New records the schema version after its migrations; a newer Studio's schema is accepted as long as it still declares this build a reader (see models.SchemaVersion). The error wraps ErrSchemaMissing, ErrSchemaTooOld or ErrSchemaTooNew, or is the database's own.
func OpenDatabase ¶
OpenDatabase connects to the database conf names (DatabaseType "postgres", or "sqlite" once pkg/studio/sqlitedb is imported, at DatabaseURL) and checks it responds. With DatabaseSchema set (postgres only) Studio's tables live in that schema: OpenDatabase creates it when missing and pins every connection's search_path to it alone. The result is what Options.DB takes. A host opens Studio's database with it rather than with gorm itself: Studio builds with its own copy of gorm (third_party/gorm.io), which the host's gorm cannot stand in for. The caller closes it after Stop, through DB().
Types ¶
type Authenticator ¶
type Authenticator = auth.Authenticator
Authenticator authenticates a request on the host's behalf. It returns nil and no error when the request carries no host identity, and an error to reject the request.
type ControlPlane ¶
type ControlPlane struct {
// contains filtered or unexported fields
}
ControlPlane is a headless AI Studio control plane: a replica that holds edge (microgateway) gRPC streams next to a full Studio sharing its database. It serves edges their configuration snapshots, delivers pushes to the edges whose streams it holds, records the analytics they send, and relays events between its edges and the other replicas. It runs no API, UI, gateway, plugins, marketplace, scheduler or telemetry, never migrates the database, and never takes the leader lease, so singleton work (budget blocks and budget.sync, marketplace sync, telemetry) stays with the full Studio and reaches this replica's edges through the relay.
Only one ControlPlane or Studio may run in a process at a time.
func NewControlPlane ¶
func NewControlPlane(opts ControlPlaneOptions) (_ *ControlPlane, err error)
NewControlPlane starts a headless control plane on a database a full Studio has migrated. It refuses a schema this build does not understand (CheckSchema), joins the cluster as a replica that never leads, and builds the gRPC control server; nothing listens until Serve.
func (*ControlPlane) NodeID ¶
func (c *ControlPlane) NodeID() string
NodeID is this replica's ID in the cluster registry.
func (*ControlPlane) Serve ¶
func (c *ControlPlane) Serve(listener net.Listener) error
Serve serves the gRPC control server for edge gateways on listener, or on Config.GRPCHost:GRPCPort when listener is nil. It blocks until Stop is called or serving fails.
func (*ControlPlane) Stop ¶
func (c *ControlPlane) Stop(ctx context.Context) error
Stop shuts the control plane down in reverse order: edge streams (their unanswered pushes go back to pending for the replica each edge reconnects to), push delivery, the cluster membership, analytics, licensing. It leaves the database open for its owner to close. Stop is safe to call more than once; after it returns, NewControlPlane or New may start another instance.
type ControlPlaneOptions ¶
type ControlPlaneOptions struct {
// Config is Studio's configuration (config.Load or config.LoadFrom).
// The control plane reads the gRPC settings (GRPCHost, GRPCPort,
// GRPCAuthToken, GRPCNextAuthToken, the TLS files unless TLSConfig is
// set, BudgetSyncInterval), MicrogatewayEncryptionKey, SecretKey,
// LicenseKey, AnalyticsBufferSize and LogLevel. NewControlPlane installs
// it as the process-wide configuration.
Config *config.AppConf
// DB is the database a full Studio (New) migrates and runs on: Postgres
// only, since the replicas coordinate through it. The control plane
// never changes its schema (see CheckSchema) and the caller closes it
// after Stop.
DB *gorm.DB
// Version is recorded with this replica in the cluster registry.
Version string
// Logger receives the control plane's logging. Nil logs JSON to stderr
// at Config.LogLevel (info when empty).
Logger *zerolog.Logger
// License supplies the AI Studio Enterprise licence, as for New; nil uses
// Config.LicenseKey. OnLicenceInvalid is called when it fails its
// periodic re-check; nil exits the process. The control plane sends no
// licence telemetry: the full Studio does.
License func() string
OnLicenceInvalid func(error)
// TLSConfig, when set, is the TLS configuration edges are served with,
// in place of Config's certificate and key files.
TLSConfig *tls.Config
// NodeID identifies this replica in the cluster registry (pkg/cluster)
// and as the owner of the edge streams it holds. Empty means a fresh ID
// per process.
NodeID string
// NodeLabel names this replica on the cluster status and the Edge
// Gateways page, where operators see which edges it holds (at most 64
// letters, digits, spaces and . _ - : / ( ), e.g. "mdcb-eu-1"). Empty
// means DefaultControlPlaneLabel.
NodeLabel string
// TracerProvider, Propagator and MeterProvider are the host's; when nil
// the control plane records no spans or metrics of its own. It never
// starts an exporter or serves a metrics endpoint.
TracerProvider trace.TracerProvider
Propagator propagation.TextMapPropagator
MeterProvider metric.MeterProvider
// AnalyticsSinks receive a copy of every analytics record edges send
// (as for New), besides the shared database.
AnalyticsSinks []analytics.AnalyticsHandler
}
ControlPlaneOptions configures a headless control plane. Config and DB are required.
type HostTykConnection ¶
type HostTykConnection = tykmcp.HostConnection
HostTykConnection is the Tyk Dashboard connection the host provides (see Options.HostTykConnection).
type Identity ¶
type Identity = services.HostIdentity
Identity is a user as the host has authenticated them. Subject and Email are required; Studio keeps the user's name, email, administrator status and, when Groups is not nil, group memberships in step with it.
type LicenseStatus ¶
type LicenseStatus struct {
Enterprise bool // an Enterprise build (Community Edition has no licence)
Valid bool // the licence is valid now
ExpiresAt time.Time // zero without a licence
DaysLeft int // -1 without a licence
// Entitlements are the licence's feature names.
Entitlements []string
}
LicenseStatus is the Enterprise licence as Studio holds it, for a host to show next to its own licence.
type Options ¶
type Options struct {
// Config is Studio's configuration, typically from config.Load or
// config.LoadFrom. New installs it as the process-wide configuration.
Config *config.AppConf
// DB is Studio's own database or schema. New migrates it; the caller
// owns it and closes it after Stop.
DB *gorm.DB
// Version, BuildHash and BuildTime are reported by /common/system and in
// telemetry and webhooks.
Version, BuildHash, BuildTime string
// Logger receives Studio's own logging. Nil keeps the logger package's
// current logger (logger.Init sets it up for the standalone binary).
// Packages that log through zerolog's global logger keep doing so.
Logger *zerolog.Logger
// TracerProvider and Propagator are used for Studio's spans and for
// carrying trace context to upstream providers. When TracerProvider is
// nil, Studio configures tracing from Config and installs it as the
// OpenTelemetry global, as the standalone binary does.
TracerProvider trace.TracerProvider
Propagator propagation.TextMapPropagator
// MeterProvider receives Studio's metrics, which the host then exports;
// Studio serves no metrics endpoint of its own. When nil and
// Config.MetricsEnabled is set, Studio serves Prometheus metrics at
// Config.MetricsPath.
MeterProvider metric.MeterProvider
// AnalyticsSinks receive a copy of every analytics record (chat
// records, proxy logs, tool calls, compliance events, batches from
// edges) besides Studio's own database, which budgets and spend are
// computed from. Each sink has its own bounded queue: a slow sink loses
// records (counted and logged) rather than slowing requests.
AnalyticsSinks []analytics.AnalyticsHandler
// OnLicenceInvalid is called when an Enterprise licence fails its
// periodic re-check. Nil exits the process.
OnLicenceInvalid func(error)
// License supplies the AI Studio Enterprise licence (the same JWT as
// TYK_AI_LICENSE, validated the same way) from the host's own
// settings. It is read at start and at every validity check, so a
// renewed licence takes effect without a restart; call ReloadLicense to
// apply one at once. Nil uses Config.LicenseKey.
License func() string
// UIAssets is the built admin frontend, rooted at its build directory
// (for example os.DirFS over the unpacked tyk-ai-studio-ui release
// tarball). Nil uses the frontend embedded in package ui, which a build
// with the studio_noui tag leaves out.
UIAssets fs.FS
// NodeID identifies this replica among the Studio replicas sharing the
// database (pkg/cluster): it is recorded as the owner of the edge streams
// this replica holds and of the work it claims. Empty means a fresh ID
// (hostname, pid and a random suffix) per process, which is what a
// replica restart needs: it must not inherit its predecessor's claims.
NodeID string
// NodeLabel names this replica on the cluster status and the Edge
// Gateways page (at most 64 letters, digits, spaces and . _ - : / ( ),
// e.g. "dashboard"). Empty means DefaultNodeLabel.
NodeLabel string
// SkipLLMDefaults skips seeding the default LLM configurations and
// their secrets.
SkipLLMDefaults bool
// Auth, when set, authenticates every request on the host's behalf:
// Studio provisions a user for each identity it returns (see Identity)
// and switches off its own password login, registration and SSO. API
// keys still authenticate requests Auth has no identity for.
Auth Authenticator
// LoginURL and LogoutURL are where the console sends a user to sign in
// or out when Auth is set. The console replaces "{return_to}" anywhere
// in LoginURL (e.g. "/login?next={return_to}") with the page the user
// asked for, URL-encoded: a path on Studio's origin under the base path,
// with its query and hash. The host validates it and returns the user
// there after signing in. A LoginURL without it is used as it is.
LoginURL, LogoutURL string
// CSRF, when set, replaces Studio's CSRF protection for
// cookie-authenticated requests with the host's. It must call the
// handler it wraps only for requests that pass. CSRFTokenHeader and
// CSRFTokenURL tell the console how to obtain and present the token.
CSRF func(http.Handler) http.Handler
CSRFTokenHeader string
CSRFTokenURL string
// GRPCTLSConfig, when set, is the TLS configuration the gRPC control
// server serves edges with, in place of Config's certificate and key
// files (Config.GRPCTLSCertPath, GRPCTLSKeyPath): a host with its own
// certificate store, cipher suites or minimum version passes its own.
GRPCTLSConfig *tls.Config
// Chromeless makes the console render pages only: no top bar (the
// Admin / Portal / Chat switch and the user menu) and no navigation
// drawers, because the host draws its own. Sticky page headers then sit
// at the top of the page instead of below Studio's 64px bar.
Chromeless bool
// HostTykConnection, when set, is the Tyk Dashboard the host is (or
// fronts), for the Tyk Dashboard MCP integration (Enterprise). Studio
// keeps one host-managed connection for it under a stable key, so
// replicas sharing the database upsert the same one; it probes the
// Dashboard and activates the connection itself, since the host is
// trusted, retrying in the background until the Dashboard answers. The
// host's fields are read-only in Studio's administration, and Studio
// asks Token for the Dashboard key on every request, so rotating it is
// the host's business. An administrator may still disable the
// connection; Studio then leaves it disabled.
HostTykConnection *HostTykConnection
}
Options configures a Studio. Config and DB are required.
type Studio ¶
type Studio struct {
// contains filtered or unexported fields
}
Studio is a running AI Studio instance.
func New ¶
New builds a Studio: it migrates the database, seeds defaults, starts the background services and builds the HTTP API, the gateway and (in control mode) the gRPC control server. Nothing listens until the caller serves HTTPHandler or calls ListenAndServe, StartProxy or StartGRPC.
func (*Studio) HTTPHandler ¶
HTTPHandler returns the admin API and UI handler: the portal, chat, management API and admin interface. Mount it at Config.BasePath (or the root when that is empty); it strips the base path itself, so the host passes requests through unchanged.
func (*Studio) LicenseStatus ¶
func (s *Studio) LicenseStatus() LicenseStatus
LicenseStatus reports the licence Studio holds.
func (*Studio) ListenAndServe ¶
ListenAndServe serves HTTPHandler on addr, with TLS when certFile and keyFile are set. It blocks until Stop is called, returning http.ErrServerClosed, or until serving fails.
func (*Studio) OAuthMetadataHandler ¶
OAuthMetadataHandler serves Studio's OAuth authorization server metadata for MCP clients. With a base path, RFC 8414 discovery happens outside it, at /.well-known/oauth-authorization-server followed by the base path, so the host mounts this handler there.
func (*Studio) ProxyHandler ¶
ProxyHandler returns the embedded AI gateway's handler for a host that serves it itself. Call StartProxy as well: the gateway's /ai/ routes hop to its own listener on Config.ProxyPort.
func (*Studio) ReloadLicense ¶
ReloadLicense re-reads the licence (Options.License, or Config.LicenseKey) and validates it now, for a host that has just stored a renewed one. Community Edition has no licence and returns nil. An error leaves the licence Studio already holds in place; the host can show it.
func (*Studio) StartGRPC ¶
StartGRPC serves the gRPC control server for edge gateways on listener, or on Config.GRPCHost:GRPCPort when listener is nil. It blocks until Stop is called or serving fails, and returns ErrNotControlPlane unless Config.GatewayMode is "control".
func (*Studio) StartProxy ¶
StartProxy serves the embedded AI gateway on Config.ProxyPort. It blocks until Stop is called, returning http.ErrServerClosed, or until serving fails. It returns ErrGatewayNotLicensed at once when the licence does not include the gateway.
func (*Studio) Stop ¶
Stop shuts Studio down: the HTTP API, the gateway, the gRPC control server, plugins and background workers, then licensing. It leaves the database open for its owner to close. ctx bounds the HTTP servers' graceful shutdown. Stop is safe to call more than once; after it returns, New may build another Studio.